@intellectif/lk-core 0.14.1 → 0.15.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 +25 -0
- package/README.md +1 -0
- package/dist/{activity-BVkg9VDo.d.cts → activity-C7cR8U9n.d.cts} +35 -2
- package/dist/{activity-BVkg9VDo.d.ts → activity-C7cR8U9n.d.ts} +35 -2
- package/dist/{chunk-KTDOZR7Y.js → chunk-54XYQ3CS.js} +2 -2
- package/dist/{chunk-J5OIRIOW.cjs → chunk-5KWF34LJ.cjs} +31 -31
- package/dist/{chunk-J5OIRIOW.cjs.map → chunk-5KWF34LJ.cjs.map} +1 -1
- package/dist/{chunk-E7LPIZXI.cjs → chunk-7ETU6EXQ.cjs} +4 -4
- package/dist/{chunk-E7LPIZXI.cjs.map → chunk-7ETU6EXQ.cjs.map} +1 -1
- package/dist/{chunk-6RND47QE.js → chunk-DPHX4E7X.js} +62 -3
- package/dist/chunk-DPHX4E7X.js.map +1 -0
- package/dist/{chunk-NBADQ3JU.js → chunk-EEMVWBPP.js} +195 -4
- package/dist/chunk-EEMVWBPP.js.map +1 -0
- package/dist/{chunk-RTIR6R2U.js → chunk-L6YBQXSB.js} +2 -2
- package/dist/{chunk-VCXIPLEW.cjs → chunk-QFWEF5ST.cjs} +63 -4
- package/dist/chunk-QFWEF5ST.cjs.map +1 -0
- package/dist/chunk-W7AQP6AV.cjs +418 -0
- package/dist/chunk-W7AQP6AV.cjs.map +1 -0
- package/dist/{index-v2_-Fyn3.d.ts → index-BWyMDlUS.d.cts} +367 -2
- package/dist/{index-BE1bM2Wi.d.cts → index-D10NaztZ.d.ts} +367 -2
- package/dist/index.cjs +397 -96
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +103 -7
- package/dist/index.d.ts +103 -7
- package/dist/index.js +310 -9
- package/dist/index.js.map +1 -1
- package/dist/schemas.cjs +11 -3
- package/dist/schemas.cjs.map +1 -1
- package/dist/schemas.d.cts +2 -2
- package/dist/schemas.d.ts +2 -2
- package/dist/schemas.js +10 -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-2NIALQEH.cjs +0 -227
- package/dist/chunk-2NIALQEH.cjs.map +0 -1
- package/dist/chunk-6RND47QE.js.map +0 -1
- package/dist/chunk-NBADQ3JU.js.map +0 -1
- package/dist/chunk-VCXIPLEW.cjs.map +0 -1
- /package/dist/{chunk-KTDOZR7Y.js.map → chunk-54XYQ3CS.js.map} +0 -0
- /package/dist/{chunk-RTIR6R2U.js.map → chunk-L6YBQXSB.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @intellectif/lk-core
|
|
2
2
|
|
|
3
|
+
## 0.15.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 5a80ea8: Interactive video: an item group whose stimulus is a video can now carry a `timeline` — quizzes at
|
|
8
|
+
moments of the video, each holding one or more of the group's own questions.
|
|
9
|
+
|
|
10
|
+
Additive throughout. `MediaTimeline`, `TimelineCue`, `TimelineChapter`, `MediaProgress` and
|
|
11
|
+
`MediaTrack` are new types; `ItemGroup.timeline`, `ActivityMedia.tracks` and `ActivityMedia.poster`
|
|
12
|
+
are new optional fields; `SequenceSlot.group` now carries the `cue` a question sits in, and
|
|
13
|
+
`AttemptPlanDrift` reports a moved quiz as `changedCueSlotIds`.
|
|
14
|
+
|
|
15
|
+
The schema places every question in exactly one quiz, keeps quiz ids unique, orders chapters, allows
|
|
16
|
+
only the five types a video can ask (multiple choice, fill-in-the-blanks, gap select, dictation,
|
|
17
|
+
read-aloud) and refuses a dictation with no recording of its own. `INTERACTIVE_VIDEO_ITEM_TYPES` and
|
|
18
|
+
`isInteractiveVideoItemType` are that list as data. `readMediaProgress` reads a stored resume point
|
|
19
|
+
without trusting it, `composeTimelineScore` folds a video's questions into an attempt's score, and
|
|
20
|
+
`createInteractiveVideoDraft` plus the `ig_timeline_*` authoring codes cover the editor.
|
|
21
|
+
|
|
22
|
+
Redaction carries the timeline, the poster and the caption tracks through: they are learner-visible
|
|
23
|
+
by definition. Nothing changed shape, so an older build that has never heard of a timeline drops it
|
|
24
|
+
and renders the same testlet with the same slots and the same grades.
|
|
25
|
+
|
|
26
|
+
See `docs/interactive-video.md`.
|
|
27
|
+
|
|
3
28
|
## 0.14.1
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ npm install @intellectif/lk-core
|
|
|
15
15
|
## What's in the box
|
|
16
16
|
|
|
17
17
|
- **Activity schemas** for `multiple-choice`, `fill-in-the-blanks`, `written-response`, `gap-select`, `dictation` and `read-aloud`, with semantic refinements (e.g. *at least one correct option*, *single-select ⇒ exactly one correct*, *passage ↔ blank-id bijection*, *image/embed require `alt`*, *a dictation title never contains its whole transcript as words*, *a read-aloud's text leaves at least one word once punctuation is folded away*). Loose at every level, so your sidecar fields survive validation.
|
|
18
|
+
- **Interactive video** — an item group whose stimulus is a video can carry a `timeline`: quizzes at moments of the video, each holding questions of that group. The schema places every question in exactly one quiz, keeps the five embeddable types honest and refuses a dictation with no recording of its own; `readMediaProgress` reads a stored resume point without trusting it, and `composeTimelineScore` folds the video's questions into an attempt's score. See [docs/interactive-video.md](https://github.com/intellectif/learning-kit/blob/main/docs/interactive-video.md).
|
|
18
19
|
- **`validateActivity(type, data)`** — the authoritative runtime validator. Returns a typed `{ success, data }` or a structured `{ success: false, errors[] }`, and **throws `UnknownActivityTypeError` if `type` is not registered** — so guard it when validating a content bank that may carry types this build does not know. `validateItemGroup(data)` is the equivalent for a `Stimulus` + `ItemGroup` container, which `validateActivity` cannot take.
|
|
19
20
|
- **`validateDraft(type, draft)`** — for editors. Reports `complete`, `incomplete` (something not written yet) or `invalid` (something wrong), each issue with a `severity` and a `code` — documented for every problem the checks recognise — and deliberately no `success` boolean to misread. Stricter than `validateActivity`, never looser. **`createDraft(type, { newId })`** returns an empty draft to start from; a multiple-choice draft marks no option correct.
|
|
20
21
|
- **An open type system** — `defineActivityType` / `registerActivityType` make an activity type a *value*, not a hard-coded union member: register one and `validateActivity`, `validateDraft`, `score`, `evaluate`, `redact` and `jsonSchemaFor` all work for it, with no SDK release. An `authoring` block on the descriptor gives it draft support.
|
|
@@ -384,6 +384,18 @@ interface ActivityDataMap {
|
|
|
384
384
|
type ActivityType = keyof ActivityDataMap;
|
|
385
385
|
/** Union of all valid activity data shapes. */
|
|
386
386
|
type ActivityData = ActivityDataMap[ActivityType];
|
|
387
|
+
/** A text track for an `audio` or `video` recording. */
|
|
388
|
+
interface MediaTrack {
|
|
389
|
+
kind: 'captions' | 'subtitles';
|
|
390
|
+
/** WebVTT URL. A player may fetch it through a loader the host supplies, with the host's credentials. */
|
|
391
|
+
src: string;
|
|
392
|
+
/** BCP 47 language tag of the text (`en`, `es`, `pt-BR`). */
|
|
393
|
+
srclang: string;
|
|
394
|
+
/** Shown in the captions menu. Non-empty, at most 60 characters. */
|
|
395
|
+
label: string;
|
|
396
|
+
/** Selected when the learner has chosen no language yet. At most one track may be the default. */
|
|
397
|
+
default?: boolean;
|
|
398
|
+
}
|
|
387
399
|
/**
|
|
388
400
|
* Optional media (image/audio/video) shown above a question or passage.
|
|
389
401
|
* URL-only: hosting/delivery is the consuming application's responsibility.
|
|
@@ -400,6 +412,15 @@ interface ActivityMedia {
|
|
|
400
412
|
alt?: string;
|
|
401
413
|
/** Optional WebVTT captions track URL for `audio`/`video`. */
|
|
402
414
|
captionsUrl?: string;
|
|
415
|
+
/**
|
|
416
|
+
* Text tracks for `audio`/`video`, one per language: what `captionsUrl`
|
|
417
|
+
* cannot say — a language, a label, more than one. When present, a player
|
|
418
|
+
* that reads it ignores `captionsUrl`. At most 12, one `default`, and no two
|
|
419
|
+
* sharing a `kind` and a `srclang`.
|
|
420
|
+
*/
|
|
421
|
+
tracks?: MediaTrack[];
|
|
422
|
+
/** An image shown before the first play. `video` only. */
|
|
423
|
+
poster?: string;
|
|
403
424
|
/**
|
|
404
425
|
* How the recording may be played. `audio` only — see
|
|
405
426
|
* {@link MediaPlaybackPolicy}. Absent means today's behaviour exactly: the
|
|
@@ -1283,7 +1304,19 @@ type InteractionKind = 'option-selected' | 'option-deselected' | 'blank-filled'
|
|
|
1283
1304
|
/** A stored take was sent for assessment. */
|
|
1284
1305
|
| 'assessment-requested'
|
|
1285
1306
|
/** An assessment request failed: the take has no grade. */
|
|
1286
|
-
| 'assessment-failed'
|
|
1307
|
+
| 'assessment-failed'
|
|
1308
|
+
/** Interactive video: the learner started playback. */
|
|
1309
|
+
| 'video-played'
|
|
1310
|
+
/** Interactive video: playback paused, by the learner or by a quiz. */
|
|
1311
|
+
| 'video-paused'
|
|
1312
|
+
/** Interactive video: the playhead moved (`from`, `to`, and whether navigation `clamped` it). */
|
|
1313
|
+
| 'video-seeked' | 'video-rate-changed' | 'video-ended'
|
|
1314
|
+
/** Interactive video: a quiz opened (`cueId`, `at`). */
|
|
1315
|
+
| 'video-quiz-opened'
|
|
1316
|
+
/** Interactive video: one question of an open quiz was shown (`cueId`, `slotId`). */
|
|
1317
|
+
| 'video-quiz-question-shown' | 'video-quiz-skipped' | 'video-quiz-closed'
|
|
1318
|
+
/** Interactive video: captions turned on (the track's `srclang`) or off (`null`). */
|
|
1319
|
+
| 'video-captions-changed' | 'video-fullscreen-changed' | 'video-pip-changed' | (string & {});
|
|
1287
1320
|
/** Fired by activity components on every discrete learner interaction. */
|
|
1288
1321
|
interface InteractionEvent {
|
|
1289
1322
|
/** The kind of interaction that occurred. */
|
|
@@ -1296,4 +1329,4 @@ interface InteractionEvent {
|
|
|
1296
1329
|
payload: Record<string, unknown>;
|
|
1297
1330
|
}
|
|
1298
1331
|
|
|
1299
|
-
export { type
|
|
1332
|
+
export { type ValidationResult as $, type ActivityData as A, type BlankConfig as B, type CriterionScore as C, type DeferredReason as D, type MultipleChoiceData as E, type FillInTheBlanksData as F, type GapSelectBank as G, type MultipleChoiceLearnerResponse as H, type InlineCorrection as I, type MultipleChoiceOption as J, type MultipleChoiceOptionMedia as K, type LearnerResponse as L, type MediaPlaybackPolicy as M, type NativeControlHint as N, type ReadAloudDimension as O, type ReadAloudDimensionWeight as P, type ReadAloudLearnerResponse as Q, type ReadAloudData as R, type ReadAloudSlowMedia as S, type RecordingBounds as T, type RecordingRef as U, type ScoringDetail as V, type ScoringOutcome as W, type ScoringResult as X, type TextMatchPolicy as Y, type TextMatchResult as Z, type ValidationError as _, type ActivityDataMap as a, type WrittenResponseData as a0, type WrittenResponseLearnerResponse as a1, type WrittenResponseRubric as a2, type WrittenResponseRubricCriterion as a3, type XAPIActor as a4, type XAPIConfig as a5, type XAPIContext as a6, type XAPIContextActivities as a7, type XAPIError as a8, type XAPIObject as a9, type XAPIResult as aa, type XAPIScore as ab, type XAPIStatement as ac, type XAPIVerbObject as ad, levenshteinDistance as ae, matchText as af, type ActivityFeedback as b, type ActivityMedia as c, type ActivityResult as d, type ActivityType as e, type DeferredScoringPartial as f, type DictationData as g, type DictationEquivalence as h, type DictationLearnerResponse as i, type DictationSlowMedia as j, type DictationTolerance as k, type FillInTheBlanksLearnerResponse as l, type GapSelectChoice as m, type GapSelectData as n, type GapSelectGap as o, type GapSelectLearnerResponse as p, type GradeRecord as q, type Grader as r, type GraderKind as s, type GraderUsage as t, type GradingState as u, type InteractionEvent as v, type InteractionKind as w, type ItemOutcome as x, type LearnerResponseMap as y, type MediaTrack as z };
|
|
@@ -384,6 +384,18 @@ interface ActivityDataMap {
|
|
|
384
384
|
type ActivityType = keyof ActivityDataMap;
|
|
385
385
|
/** Union of all valid activity data shapes. */
|
|
386
386
|
type ActivityData = ActivityDataMap[ActivityType];
|
|
387
|
+
/** A text track for an `audio` or `video` recording. */
|
|
388
|
+
interface MediaTrack {
|
|
389
|
+
kind: 'captions' | 'subtitles';
|
|
390
|
+
/** WebVTT URL. A player may fetch it through a loader the host supplies, with the host's credentials. */
|
|
391
|
+
src: string;
|
|
392
|
+
/** BCP 47 language tag of the text (`en`, `es`, `pt-BR`). */
|
|
393
|
+
srclang: string;
|
|
394
|
+
/** Shown in the captions menu. Non-empty, at most 60 characters. */
|
|
395
|
+
label: string;
|
|
396
|
+
/** Selected when the learner has chosen no language yet. At most one track may be the default. */
|
|
397
|
+
default?: boolean;
|
|
398
|
+
}
|
|
387
399
|
/**
|
|
388
400
|
* Optional media (image/audio/video) shown above a question or passage.
|
|
389
401
|
* URL-only: hosting/delivery is the consuming application's responsibility.
|
|
@@ -400,6 +412,15 @@ interface ActivityMedia {
|
|
|
400
412
|
alt?: string;
|
|
401
413
|
/** Optional WebVTT captions track URL for `audio`/`video`. */
|
|
402
414
|
captionsUrl?: string;
|
|
415
|
+
/**
|
|
416
|
+
* Text tracks for `audio`/`video`, one per language: what `captionsUrl`
|
|
417
|
+
* cannot say — a language, a label, more than one. When present, a player
|
|
418
|
+
* that reads it ignores `captionsUrl`. At most 12, one `default`, and no two
|
|
419
|
+
* sharing a `kind` and a `srclang`.
|
|
420
|
+
*/
|
|
421
|
+
tracks?: MediaTrack[];
|
|
422
|
+
/** An image shown before the first play. `video` only. */
|
|
423
|
+
poster?: string;
|
|
403
424
|
/**
|
|
404
425
|
* How the recording may be played. `audio` only — see
|
|
405
426
|
* {@link MediaPlaybackPolicy}. Absent means today's behaviour exactly: the
|
|
@@ -1283,7 +1304,19 @@ type InteractionKind = 'option-selected' | 'option-deselected' | 'blank-filled'
|
|
|
1283
1304
|
/** A stored take was sent for assessment. */
|
|
1284
1305
|
| 'assessment-requested'
|
|
1285
1306
|
/** An assessment request failed: the take has no grade. */
|
|
1286
|
-
| 'assessment-failed'
|
|
1307
|
+
| 'assessment-failed'
|
|
1308
|
+
/** Interactive video: the learner started playback. */
|
|
1309
|
+
| 'video-played'
|
|
1310
|
+
/** Interactive video: playback paused, by the learner or by a quiz. */
|
|
1311
|
+
| 'video-paused'
|
|
1312
|
+
/** Interactive video: the playhead moved (`from`, `to`, and whether navigation `clamped` it). */
|
|
1313
|
+
| 'video-seeked' | 'video-rate-changed' | 'video-ended'
|
|
1314
|
+
/** Interactive video: a quiz opened (`cueId`, `at`). */
|
|
1315
|
+
| 'video-quiz-opened'
|
|
1316
|
+
/** Interactive video: one question of an open quiz was shown (`cueId`, `slotId`). */
|
|
1317
|
+
| 'video-quiz-question-shown' | 'video-quiz-skipped' | 'video-quiz-closed'
|
|
1318
|
+
/** Interactive video: captions turned on (the track's `srclang`) or off (`null`). */
|
|
1319
|
+
| 'video-captions-changed' | 'video-fullscreen-changed' | 'video-pip-changed' | (string & {});
|
|
1287
1320
|
/** Fired by activity components on every discrete learner interaction. */
|
|
1288
1321
|
interface InteractionEvent {
|
|
1289
1322
|
/** The kind of interaction that occurred. */
|
|
@@ -1296,4 +1329,4 @@ interface InteractionEvent {
|
|
|
1296
1329
|
payload: Record<string, unknown>;
|
|
1297
1330
|
}
|
|
1298
1331
|
|
|
1299
|
-
export { type
|
|
1332
|
+
export { type ValidationResult as $, type ActivityData as A, type BlankConfig as B, type CriterionScore as C, type DeferredReason as D, type MultipleChoiceData as E, type FillInTheBlanksData as F, type GapSelectBank as G, type MultipleChoiceLearnerResponse as H, type InlineCorrection as I, type MultipleChoiceOption as J, type MultipleChoiceOptionMedia as K, type LearnerResponse as L, type MediaPlaybackPolicy as M, type NativeControlHint as N, type ReadAloudDimension as O, type ReadAloudDimensionWeight as P, type ReadAloudLearnerResponse as Q, type ReadAloudData as R, type ReadAloudSlowMedia as S, type RecordingBounds as T, type RecordingRef as U, type ScoringDetail as V, type ScoringOutcome as W, type ScoringResult as X, type TextMatchPolicy as Y, type TextMatchResult as Z, type ValidationError as _, type ActivityDataMap as a, type WrittenResponseData as a0, type WrittenResponseLearnerResponse as a1, type WrittenResponseRubric as a2, type WrittenResponseRubricCriterion as a3, type XAPIActor as a4, type XAPIConfig as a5, type XAPIContext as a6, type XAPIContextActivities as a7, type XAPIError as a8, type XAPIObject as a9, type XAPIResult as aa, type XAPIScore as ab, type XAPIStatement as ac, type XAPIVerbObject as ad, levenshteinDistance as ae, matchText as af, type ActivityFeedback as b, type ActivityMedia as c, type ActivityResult as d, type ActivityType as e, type DeferredScoringPartial as f, type DictationData as g, type DictationEquivalence as h, type DictationLearnerResponse as i, type DictationSlowMedia as j, type DictationTolerance as k, type FillInTheBlanksLearnerResponse as l, type GapSelectChoice as m, type GapSelectData as n, type GapSelectGap as o, type GapSelectLearnerResponse as p, type GradeRecord as q, type Grader as r, type GraderKind as s, type GraderUsage as t, type GradingState as u, type InteractionEvent as v, type InteractionKind as w, type ItemOutcome as x, type LearnerResponseMap as y, type MediaTrack as z };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ActivitySchemaError,
|
|
3
3
|
getActivityTypeDescriptor
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-DPHX4E7X.js";
|
|
5
5
|
|
|
6
6
|
// src/xapi/validators.ts
|
|
7
7
|
import { z } from "zod/v4";
|
|
@@ -269,4 +269,4 @@ export {
|
|
|
269
269
|
xAPIBuilder,
|
|
270
270
|
xapiDefinitionFor
|
|
271
271
|
};
|
|
272
|
-
//# sourceMappingURL=chunk-
|
|
272
|
+
//# sourceMappingURL=chunk-54XYQ3CS.js.map
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
var
|
|
17
|
+
var _chunkQFWEF5STcjs = require('./chunk-QFWEF5ST.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 _chunkQFWEF5STcjs.alignSequences.call(void 0, referencePoints, attemptPoints).map(
|
|
303
303
|
({ step, referenceIndex, attemptIndex }) => {
|
|
304
|
-
if (step ===
|
|
304
|
+
if (step === _chunkQFWEF5STcjs.STEP.missing) {
|
|
305
305
|
return { op: "missing", reference: referencePoints[referenceIndex], attempt: "" };
|
|
306
306
|
}
|
|
307
|
-
if (step ===
|
|
307
|
+
if (step === _chunkQFWEF5STcjs.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 = _chunkQFWEF5STcjs.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 += _chunkQFWEF5STcjs.codePointLength.call(void 0, text) + (length === 0 ? 0 : 1);
|
|
336
|
+
if (!inserted && length <= _chunkQFWEF5STcjs.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(_chunkQFWEF5STcjs.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(_chunkQFWEF5STcjs.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 > _chunkQFWEF5STcjs.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 ${_chunkQFWEF5STcjs.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 > _chunkQFWEF5STcjs.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 ${_chunkQFWEF5STcjs.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 = _chunkQFWEF5STcjs.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 _chunkQFWEF5STcjs.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 === _chunkQFWEF5STcjs.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 === _chunkQFWEF5STcjs.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: _chunkQFWEF5STcjs.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 _chunkQFWEF5STcjs.countWords.call(void 0, recognized);
|
|
620
620
|
}
|
|
621
|
-
return
|
|
621
|
+
return _chunkQFWEF5STcjs.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 = _chunkQFWEF5STcjs.ReadAloudDataSchema.safeParse(data);
|
|
646
646
|
if (!parsed.success) {
|
|
647
|
-
throw new (0,
|
|
647
|
+
throw new (0, _chunkQFWEF5STcjs.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, _chunkQFWEF5STcjs.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 _chunkQFWEF5STcjs.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 = _chunkQFWEF5STcjs.getActivityTypeDescriptor.call(void 0, activityType);
|
|
953
953
|
if (descriptor === void 0) {
|
|
954
|
-
throw new (0,
|
|
954
|
+
throw new (0, _chunkQFWEF5STcjs.UnknownActivityTypeError)(String(activityType));
|
|
955
955
|
}
|
|
956
956
|
if (descriptor.scoring.kind === "deferred") {
|
|
957
|
-
throw new (0,
|
|
957
|
+
throw new (0, _chunkQFWEF5STcjs.DeferredScoringError)(descriptor.type);
|
|
958
958
|
}
|
|
959
959
|
if (isRedacted(activityData)) {
|
|
960
|
-
throw new (0,
|
|
960
|
+
throw new (0, _chunkQFWEF5STcjs.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, _chunkQFWEF5STcjs.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" ? _chunkQFWEF5STcjs.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-5KWF34LJ.cjs.map
|