@intellectif/lk-core 0.11.0 → 0.13.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 +38 -0
- package/README.md +2 -1
- package/dist/{activity-B2QxK15j.d.cts → activity-DfAmJ1sl.d.cts} +134 -7
- package/dist/{activity-B2QxK15j.d.ts → activity-DfAmJ1sl.d.ts} +134 -7
- package/dist/{chunk-3FEAEO3D.cjs → chunk-DQKJUMUW.cjs} +38 -15
- package/dist/chunk-DQKJUMUW.cjs.map +1 -0
- package/dist/{chunk-ZTY4UIUD.js → chunk-FGN4LLSP.js} +50 -6
- package/dist/chunk-FGN4LLSP.js.map +1 -0
- package/dist/{chunk-VTTRXGDD.cjs → chunk-FT7SAC3X.cjs} +4 -4
- package/dist/{chunk-VTTRXGDD.cjs.map → chunk-FT7SAC3X.cjs.map} +1 -1
- package/dist/{chunk-IXXMYJI7.js → chunk-PYP3HCRQ.js} +2 -2
- package/dist/{chunk-VBM5H6P5.cjs → chunk-UHQVIC3P.cjs} +58 -14
- package/dist/chunk-UHQVIC3P.cjs.map +1 -0
- package/dist/{chunk-FIS5KBCE.js → chunk-XKKKWKRH.js} +1973 -179
- package/dist/chunk-XKKKWKRH.js.map +1 -0
- package/dist/{chunk-7ODQRNR4.cjs → chunk-XONKWL6D.cjs} +1812 -18
- package/dist/chunk-XONKWL6D.cjs.map +1 -0
- package/dist/{chunk-XGCTAQSS.js → chunk-Y36S3X3L.js} +26 -3
- package/dist/chunk-Y36S3X3L.js.map +1 -0
- package/dist/{index-CRKtWP5G.d.ts → index-Bz9wUigA.d.cts} +181 -4
- package/dist/{index-B_scInLM.d.cts → index-CJbbVE9o.d.ts} +181 -4
- package/dist/index.cjs +349 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -6
- package/dist/index.d.ts +48 -6
- package/dist/index.js +322 -11
- package/dist/index.js.map +1 -1
- package/dist/schemas.cjs +17 -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 +16 -2
- package/dist/scoring.cjs +19 -3
- package/dist/scoring.cjs.map +1 -1
- package/dist/scoring.d.cts +150 -5
- package/dist/scoring.d.ts +150 -5
- package/dist/scoring.js +18 -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/vectors/README.md +9 -1
- package/vectors/scoring.json +12023 -1
- package/dist/chunk-3FEAEO3D.cjs.map +0 -1
- package/dist/chunk-7ODQRNR4.cjs.map +0 -1
- package/dist/chunk-FIS5KBCE.js.map +0 -1
- package/dist/chunk-VBM5H6P5.cjs.map +0 -1
- package/dist/chunk-XGCTAQSS.js.map +0 -1
- package/dist/chunk-ZTY4UIUD.js.map +0 -1
- /package/dist/{chunk-IXXMYJI7.js.map → chunk-PYP3HCRQ.js.map} +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ScoredItem } from './scoring.js';
|
|
2
|
-
export { AssessmentScore, AssessmentSectionInput, Band, CompositionPolicy, DEFAULT_PASS_THRESHOLD, PassFailureReason, RoundingMode, RoundingPolicy, SectionScore, classifyBand, composeAssessmentScore, computePassThreshold, evaluate, gte, roundGrade, score } from './scoring.js';
|
|
3
|
-
import {
|
|
4
|
-
export { b as ActivityFeedback, d as ActivityResult, B as BlankConfig,
|
|
5
|
-
import {
|
|
6
|
-
export { B as BlankConfigSchema, F as FeedbackSchema,
|
|
2
|
+
export { AssessmentScore, AssessmentSectionInput, Band, CompositionPolicy, DEFAULT_PASS_THRESHOLD, DICTATION_MAX_ACCEPTED_TRANSCRIPTS, DICTATION_MAX_EQUIVALENCES, DICTATION_MAX_EQUIVALENCE_LENGTH, DICTATION_MAX_TEXT_LENGTH, DICTATION_MAX_TRANSCRIPT_LENGTH, DictationAlignment, DictationCharOp, DictationReference, DictationWordAlignment, PassFailureReason, RoundingMode, RoundingPolicy, ScoringOptions, SectionScore, alignDictation, classifyBand, composeAssessmentScore, computePassThreshold, dictationReferenceWords, diffDictationChars, evaluate, gte, roundGrade, score } from './scoring.js';
|
|
3
|
+
import { w as ItemOutcome, A as ActivityData, L as LearnerResponse, V as ValidationError, e as ActivityType, a as ActivityDataMap, C as CriterionScore, p as GradeRecord, N as NativeControlHint, c as ActivityMedia, K as ScoringResult, D as DeferredScoringPartial, f as DictationData, h as DictationLearnerResponse, F as FillInTheBlanksData, k as FillInTheBlanksLearnerResponse, m as GapSelectData, o as GapSelectLearnerResponse, y as MultipleChoiceData, z as MultipleChoiceLearnerResponse, W as WrittenResponseData, Q as WrittenResponseLearnerResponse } from './activity-DfAmJ1sl.js';
|
|
4
|
+
export { b as ActivityFeedback, d as ActivityResult, B as BlankConfig, g as DictationEquivalence, i as DictationSlowMedia, j as DictationTolerance, G as GapSelectBank, l as GapSelectChoice, n as GapSelectGap, q as Grader, r as GraderKind, s as GraderUsage, t as GradingState, I as InlineCorrection, u as InteractionEvent, v as InteractionKind, x as LearnerResponseMap, M as MediaPlaybackPolicy, E as MultipleChoiceOption, H as MultipleChoiceOptionMedia, S as ScoringDetail, J as ScoringOutcome, T as TextMatchPolicy, O as TextMatchResult, P as ValidationResult, R as WrittenResponseRubric, U as WrittenResponseRubricCriterion, X as XAPIActor, Y as XAPIConfig, Z as XAPIContext, _ as XAPIContextActivities, $ as XAPIError, a0 as XAPIObject, a1 as XAPIResult, a2 as XAPIScore, a3 as XAPIStatement, a4 as XAPIVerbObject, a5 as levenshteinDistance, a6 as matchText } from './activity-DfAmJ1sl.js';
|
|
5
|
+
import { Y as SequenceSlot, X as SequenceEntry, I as ItemGroup } from './index-CJbbVE9o.js';
|
|
6
|
+
export { B as BlankConfigSchema, D as DictationDataSchema, a as DictationEquivalenceSchema, b as DictationSlowMediaSchema, c as DictationToleranceSchema, F as FeedbackSchema, d as FillInTheBlanksDataSchema, G as GapSelectBankSchema, e as GapSelectChoiceSchema, f as GapSelectDataSchema, g as GapSelectGapSchema, h as ItemGroupSchema, M as MediaPlaybackSchema, i as MediaSchema, j as MediaUrlSchema, k as MultipleChoiceDataSchema, l as MultipleChoiceOptionMediaSchema, m as MultipleChoiceOptionSchema, N as NativeControlHintSchema, R as RedactedActivity, n as RedactedBlankConfig, o as RedactedBlankConfigSchema, p as RedactedDictationData, q as RedactedDictationDataSchema, r as RedactedDictationSlowMedia, s as RedactedDictationSlowMediaSchema, t as RedactedFillInTheBlanksData, u as RedactedFillInTheBlanksDataSchema, v as RedactedGapSelectBank, w as RedactedGapSelectBankSchema, x as RedactedGapSelectChoice, y as RedactedGapSelectChoiceSchema, z as RedactedGapSelectData, A as RedactedGapSelectDataSchema, C as RedactedGapSelectGap, E as RedactedGapSelectGapSchema, H as RedactedItemGroupSchema, J as RedactedMediaSchema, K as RedactedMultipleChoiceData, L as RedactedMultipleChoiceDataSchema, O as RedactedMultipleChoiceOption, P as RedactedMultipleChoiceOptionMedia, Q as RedactedMultipleChoiceOptionMediaSchema, S as RedactedMultipleChoiceOptionSchema, T as RedactedStimulus, U as RedactedStimulusSchema, V as RedactedWrittenResponseData, W as RedactedWrittenResponseDataSchema, Z as SequenceSlotGroup, _ as Stimulus, $ as StimulusKind, a0 as StimulusSchema, a1 as TextMatchPolicySchema, a2 as WrittenResponseDataSchema, a3 as WrittenResponseRubricCriterionSchema, a4 as WrittenResponseRubricSchema, a5 as dictationJsonSchema, a6 as fillInTheBlanksJsonSchema, a7 as gapSelectJsonSchema, a8 as itemGroupJsonSchema, a9 as jsonSchemaFor, aa as multipleChoiceJsonSchema, ab as stimulusJsonSchema, ac as validateActivity, ad as validateItemGroup, ae as writtenResponseJsonSchema } from './index-CJbbVE9o.js';
|
|
7
7
|
import { z } from 'zod/v4';
|
|
8
8
|
export { AnsweredStatementParams, CompletedStatementParams, SubmittedStatementParams, XAPIObjectParams, XAPIStatementParams, XAPIVerb, XAPIVerbKey, XAPI_VERB_DISPLAY, validateXAPIStatement, xAPIBuilder, xapiDefinitionFor } from './xapi.js';
|
|
9
9
|
|
|
@@ -382,6 +382,40 @@ interface DraftContext {
|
|
|
382
382
|
newId: () => string;
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
+
/**
|
|
386
|
+
* A new, empty reading or listening testlet for an editor to start from.
|
|
387
|
+
*
|
|
388
|
+
* The stimulus starts as `text`, the only kind that needs no media file to be
|
|
389
|
+
* a valid draft — an author who is writing a passage can begin typing, and one
|
|
390
|
+
* who is building a listening item changes `kind` before uploading. `items`
|
|
391
|
+
* starts empty, exactly as a new fill-in-the-blanks draft starts with no
|
|
392
|
+
* blanks: the group is `incomplete` until somebody writes a question into it.
|
|
393
|
+
*
|
|
394
|
+
* @throws Error when `newId` returns an empty or repeated id.
|
|
395
|
+
*/
|
|
396
|
+
declare function createItemGroupDraft(context: DraftContext): ItemGroup;
|
|
397
|
+
/**
|
|
398
|
+
* Tells an unfinished testlet from a broken one — {@link validateDraft} for the
|
|
399
|
+
* container that holds several questions around one passage or recording.
|
|
400
|
+
*
|
|
401
|
+
* It is a **separate entry point rather than a `'item-group'` type**, because
|
|
402
|
+
* `validateDraft('item-group', …)` throws by design: `item-group` is reserved
|
|
403
|
+
* in the registry precisely so it can never be registered as an activity, and
|
|
404
|
+
* that guard is what keeps `isItemGroup` and `flattenSequence` able to trust
|
|
405
|
+
* their own container. Widening `validateDraft` to accept it would have meant
|
|
406
|
+
* weakening that.
|
|
407
|
+
*
|
|
408
|
+
* Each item is checked by `validateDraft` for its own type and its issues are
|
|
409
|
+
* re-pathed under `items.N.…`, so an editor gets one list for the whole group
|
|
410
|
+
* and every code in it is the code that type already documents. An item whose
|
|
411
|
+
* type nobody registered is REPORTED, never thrown — an author fixing a
|
|
412
|
+
* six-item group wants all six problems, not the first one that blew up.
|
|
413
|
+
*
|
|
414
|
+
* The group is `complete` only when the container passes, every item passes its
|
|
415
|
+
* own schema, and every item is itself `complete`.
|
|
416
|
+
*/
|
|
417
|
+
declare function validateItemGroupDraft(draft: unknown): DraftValidationResult<ItemGroup>;
|
|
418
|
+
|
|
385
419
|
/**
|
|
386
420
|
* Tells a draft that is not finished from a draft that is wrong.
|
|
387
421
|
*
|
|
@@ -1048,6 +1082,14 @@ declare const fillInTheBlanksType: ActivityTypeDescriptor<FillInTheBlanksData, F
|
|
|
1048
1082
|
* count is informational, never trusted.
|
|
1049
1083
|
*/
|
|
1050
1084
|
declare const writtenResponseType: ActivityTypeDescriptor<WrittenResponseData, WrittenResponseLearnerResponse>;
|
|
1085
|
+
/** Built-in Gap Select descriptor. */
|
|
1086
|
+
declare const gapSelectType: ActivityTypeDescriptor<GapSelectData, GapSelectLearnerResponse>;
|
|
1087
|
+
/**
|
|
1088
|
+
* Built-in Dictation descriptor. Graded synchronously: the score is the
|
|
1089
|
+
* character-level similarity of the typed text to the transcript, a pure
|
|
1090
|
+
* computation identical in a browser and on a server.
|
|
1091
|
+
*/
|
|
1092
|
+
declare const dictationType: ActivityTypeDescriptor<DictationData, DictationLearnerResponse>;
|
|
1051
1093
|
|
|
1052
1094
|
/**
|
|
1053
1095
|
* Seeded, deterministic shuffling — the ONE algorithm every presentation-order
|
|
@@ -1148,4 +1190,4 @@ interface ThemeTokens {
|
|
|
1148
1190
|
'--lk-transition-base': string;
|
|
1149
1191
|
}
|
|
1150
1192
|
|
|
1151
|
-
export { ActivityData, ActivityDataMap, ActivityMedia, ActivitySchemaError, ActivityType, type ActivityTypeAuthoring, type ActivityTypeDescriptor, type ActivityTypeInterop, type ActivityTypeScoring, type AttemptPlan, type AttemptPlanDrift, type AttemptPlanSlot, type AttemptProgress, type AttemptState, CriterionScore, DeferredScoringError, DeferredScoringPartial, type DraftComplete, type DraftContext, type DraftIssue, type DraftNotComplete, type DraftSeverity, type DraftValidationResult, type FieldPolicy, FillInTheBlanksData, FillInTheBlanksLearnerResponse, type FlattenSequenceOptions, type GradeFromRubricOptions, GradeRecord, ItemGroup, ItemOutcome, LearnerResponse, type MediaBudgetRef, type MediaPlayClaim, type MediaPlayGrant, type MediaPlayLedger, type MediaPlayLedgerEntry, type MissingOutcomePolicy, MultipleChoiceData, MultipleChoiceLearnerResponse, type PartialScoringResult, type PlanAttemptOptions, type PlannableEntry, type RedactOptions, type RedactedActivityData, type RedactedItemGroup, RedactedScoringError, type RegisteredActivityTypeDescriptor, type ResolvedPlaybackPolicy, type ResponseDiffEntry, ScoredItem, ScoringResult, type SeededShuffleOptions, type Sensitivity, SequenceEntry, SequenceSlot, type ShuffleVersion, type ThemeTokens, UnknownActivityTypeError, ValidationError, WrittenResponseData, WrittenResponseLearnerResponse, type XAPIInteractionType, assertRedacted, assertRedactedItemGroup, canonicalJson, contentHash, countWords, createDraft, defineActivityType, diffResponses, entryKeyOf, fillInTheBlanksType, fingerprint, flattenSequence, getActivityTypeDescriptor, gradeFromRubric, hasGrade, hashSeed, isItemGroup, multipleChoiceType, outcomeFromGrade, planAttempt, planMediaBudgets, redact, redactItemGroup, registerActivityType, registeredActivityTypes, resolvePlaybackPolicy, restoreAttemptState, restoreMediaPlayLedger, scoredItemsFromPlan, seededShuffle, serializeAttemptState, serializeMediaPlayLedger, slotMediaKey, stimulusMediaKey, validateDraft, verifyAttemptPlan, writtenResponseType };
|
|
1193
|
+
export { ActivityData, ActivityDataMap, ActivityMedia, ActivitySchemaError, ActivityType, type ActivityTypeAuthoring, type ActivityTypeDescriptor, type ActivityTypeInterop, type ActivityTypeScoring, type AttemptPlan, type AttemptPlanDrift, type AttemptPlanSlot, type AttemptProgress, type AttemptState, CriterionScore, DeferredScoringError, DeferredScoringPartial, DictationData, DictationLearnerResponse, type DraftComplete, type DraftContext, type DraftIssue, type DraftNotComplete, type DraftSeverity, type DraftValidationResult, type FieldPolicy, FillInTheBlanksData, FillInTheBlanksLearnerResponse, type FlattenSequenceOptions, GapSelectData, GapSelectLearnerResponse, type GradeFromRubricOptions, GradeRecord, ItemGroup, ItemOutcome, LearnerResponse, type MediaBudgetRef, type MediaPlayClaim, type MediaPlayGrant, type MediaPlayLedger, type MediaPlayLedgerEntry, type MissingOutcomePolicy, MultipleChoiceData, MultipleChoiceLearnerResponse, NativeControlHint, type PartialScoringResult, type PlanAttemptOptions, type PlannableEntry, type RedactOptions, type RedactedActivityData, type RedactedItemGroup, RedactedScoringError, type RegisteredActivityTypeDescriptor, type ResolvedPlaybackPolicy, type ResponseDiffEntry, ScoredItem, ScoringResult, type SeededShuffleOptions, type Sensitivity, SequenceEntry, SequenceSlot, type ShuffleVersion, type ThemeTokens, UnknownActivityTypeError, ValidationError, WrittenResponseData, WrittenResponseLearnerResponse, type XAPIInteractionType, assertRedacted, assertRedactedItemGroup, canonicalJson, contentHash, countWords, createDraft, createItemGroupDraft, defineActivityType, dictationType, diffResponses, entryKeyOf, fillInTheBlanksType, fingerprint, flattenSequence, gapSelectType, getActivityTypeDescriptor, gradeFromRubric, hasGrade, hashSeed, isItemGroup, multipleChoiceType, outcomeFromGrade, planAttempt, planMediaBudgets, redact, redactItemGroup, registerActivityType, registeredActivityTypes, resolvePlaybackPolicy, restoreAttemptState, restoreMediaPlayLedger, scoredItemsFromPlan, seededShuffle, serializeAttemptState, serializeMediaPlayLedger, slotMediaKey, stimulusMediaKey, validateDraft, validateItemGroupDraft, verifyAttemptPlan, writtenResponseType };
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
classifyBand,
|
|
4
4
|
composeAssessmentScore,
|
|
5
5
|
computePassThreshold,
|
|
6
|
+
diffDictationChars,
|
|
6
7
|
evaluate,
|
|
7
8
|
gradeFromRubric,
|
|
8
9
|
gte,
|
|
@@ -10,19 +11,20 @@ import {
|
|
|
10
11
|
outcomeFromGrade,
|
|
11
12
|
roundGrade,
|
|
12
13
|
score
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-FGN4LLSP.js";
|
|
14
15
|
import {
|
|
15
16
|
XAPIVerb,
|
|
16
17
|
XAPI_VERB_DISPLAY,
|
|
17
18
|
validateXAPIStatement,
|
|
18
19
|
xAPIBuilder,
|
|
19
20
|
xapiDefinitionFor
|
|
20
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-PYP3HCRQ.js";
|
|
21
22
|
import {
|
|
22
23
|
ItemGroupSchema,
|
|
23
24
|
RedactedItemGroupSchema,
|
|
24
25
|
RedactedStimulusSchema,
|
|
25
26
|
StimulusSchema,
|
|
27
|
+
dictationJsonSchema,
|
|
26
28
|
fillInTheBlanksJsonSchema,
|
|
27
29
|
gapSelectJsonSchema,
|
|
28
30
|
itemGroupJsonSchema,
|
|
@@ -32,14 +34,24 @@ import {
|
|
|
32
34
|
validateActivity,
|
|
33
35
|
validateItemGroup,
|
|
34
36
|
writtenResponseJsonSchema
|
|
35
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-Y36S3X3L.js";
|
|
36
38
|
import {
|
|
37
39
|
ActivitySchemaError,
|
|
38
40
|
BlankConfigSchema,
|
|
41
|
+
DICTATION_MAX_ACCEPTED_TRANSCRIPTS,
|
|
42
|
+
DICTATION_MAX_EQUIVALENCES,
|
|
43
|
+
DICTATION_MAX_EQUIVALENCE_LENGTH,
|
|
44
|
+
DICTATION_MAX_TEXT_LENGTH,
|
|
45
|
+
DICTATION_MAX_TRANSCRIPT_LENGTH,
|
|
39
46
|
DRAFT_ISSUE_SEVERITY,
|
|
40
47
|
DeferredScoringError,
|
|
48
|
+
DictationDataSchema,
|
|
49
|
+
DictationEquivalenceSchema,
|
|
50
|
+
DictationSlowMediaSchema,
|
|
51
|
+
DictationToleranceSchema,
|
|
41
52
|
FeedbackSchema,
|
|
42
53
|
FillInTheBlanksDataSchema,
|
|
54
|
+
GROUP_CAPTIONS_REVEAL_DICTATION,
|
|
43
55
|
GapSelectBankSchema,
|
|
44
56
|
GapSelectChoiceSchema,
|
|
45
57
|
GapSelectDataSchema,
|
|
@@ -53,6 +65,8 @@ import {
|
|
|
53
65
|
MultipleChoiceOptionSchema,
|
|
54
66
|
NativeControlHintSchema,
|
|
55
67
|
RedactedBlankConfigSchema,
|
|
68
|
+
RedactedDictationDataSchema,
|
|
69
|
+
RedactedDictationSlowMediaSchema,
|
|
56
70
|
RedactedFillInTheBlanksDataSchema,
|
|
57
71
|
RedactedGapSelectBankSchema,
|
|
58
72
|
RedactedGapSelectChoiceSchema,
|
|
@@ -69,20 +83,33 @@ import {
|
|
|
69
83
|
WrittenResponseDataSchema,
|
|
70
84
|
WrittenResponseRubricCriterionSchema,
|
|
71
85
|
WrittenResponseRubricSchema,
|
|
86
|
+
alignDictation,
|
|
87
|
+
checkMedia,
|
|
72
88
|
countWords,
|
|
89
|
+
coveredPathsOf,
|
|
90
|
+
covers,
|
|
73
91
|
defineActivityType,
|
|
92
|
+
dictationReferenceWords,
|
|
93
|
+
dictationType,
|
|
74
94
|
fillInTheBlanksType,
|
|
95
|
+
gapSelectType,
|
|
75
96
|
getActivityTypeDescriptor,
|
|
97
|
+
groupCaptionsRevealDictation,
|
|
98
|
+
isMissingId,
|
|
76
99
|
isRecord,
|
|
100
|
+
isUnset,
|
|
101
|
+
isUnwritten,
|
|
77
102
|
issue,
|
|
78
103
|
levenshteinDistance,
|
|
79
104
|
matchText,
|
|
80
105
|
multipleChoiceType,
|
|
106
|
+
pathKey,
|
|
81
107
|
refusesEmpty,
|
|
82
108
|
registerActivityType,
|
|
83
109
|
registeredActivityTypes,
|
|
110
|
+
withValidationScope,
|
|
84
111
|
writtenResponseType
|
|
85
|
-
} from "./chunk-
|
|
112
|
+
} from "./chunk-XKKKWKRH.js";
|
|
86
113
|
|
|
87
114
|
// src/content-hash.ts
|
|
88
115
|
function canonicalJson(value, seen = /* @__PURE__ */ new Set()) {
|
|
@@ -586,8 +613,278 @@ function diffResponses(before, after) {
|
|
|
586
613
|
return entries;
|
|
587
614
|
}
|
|
588
615
|
|
|
616
|
+
// src/authoring/item-group.ts
|
|
617
|
+
function createItemGroupDraft(context) {
|
|
618
|
+
const issued = /* @__PURE__ */ new Set();
|
|
619
|
+
const newId = () => {
|
|
620
|
+
const id = context.newId();
|
|
621
|
+
if (typeof id !== "string" || id === "") {
|
|
622
|
+
throw new Error("createItemGroupDraft: newId() must return a non-empty string.");
|
|
623
|
+
}
|
|
624
|
+
if (issued.has(id)) {
|
|
625
|
+
throw new Error(
|
|
626
|
+
`createItemGroupDraft: newId() returned "${id}" twice. Ids within one draft must differ.`
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
issued.add(id);
|
|
630
|
+
return id;
|
|
631
|
+
};
|
|
632
|
+
return {
|
|
633
|
+
schemaVersion: "1.0",
|
|
634
|
+
type: "item-group",
|
|
635
|
+
id: newId(),
|
|
636
|
+
stimulus: { id: newId(), kind: "text", body: "" },
|
|
637
|
+
items: []
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
function validateItemGroupDraft(draft) {
|
|
641
|
+
return withValidationScope(() => validateItemGroupDraftInScope(draft));
|
|
642
|
+
}
|
|
643
|
+
function validateItemGroupDraftInScope(draft) {
|
|
644
|
+
const issues = [];
|
|
645
|
+
if (!isRecord(draft)) {
|
|
646
|
+
issues.push(
|
|
647
|
+
issue("ig_not_an_object", [], "A group must be an object with a stimulus and its items.")
|
|
648
|
+
);
|
|
649
|
+
return { status: "invalid", issues };
|
|
650
|
+
}
|
|
651
|
+
issues.push(...checkGroupIdentity(draft));
|
|
652
|
+
if (isRecord(draft.stimulus)) {
|
|
653
|
+
issues.push(...checkStimulus(draft.stimulus));
|
|
654
|
+
} else if (isUnset(draft.stimulus)) {
|
|
655
|
+
issues.push(
|
|
656
|
+
issue(
|
|
657
|
+
"ig_stimulus_required",
|
|
658
|
+
["stimulus"],
|
|
659
|
+
"Add the passage, recording or image the questions are about."
|
|
660
|
+
)
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
issues.push(...checkItems(draft));
|
|
664
|
+
if (groupCaptionsRevealDictation(draft, (captionsUrl) => !isUnwritten(captionsUrl))) {
|
|
665
|
+
issues.push(
|
|
666
|
+
issue(
|
|
667
|
+
"dc_captions_not_allowed",
|
|
668
|
+
["stimulus", "media", "captionsUrl"],
|
|
669
|
+
GROUP_CAPTIONS_REVEAL_DICTATION
|
|
670
|
+
)
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
const parsed = ItemGroupSchema.safeParse(draft, { reportInput: true });
|
|
674
|
+
if (!parsed.success) {
|
|
675
|
+
for (const schemaIssue of parsed.error.issues) {
|
|
676
|
+
const path = schemaIssue.path.map(String);
|
|
677
|
+
if (issues.some((known) => covers(known.path, path))) {
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
if (path.length > 0 && refusesEmpty(draft, schemaIssue)) {
|
|
681
|
+
issues.push(
|
|
682
|
+
issue(
|
|
683
|
+
"null_not_allowed",
|
|
684
|
+
path,
|
|
685
|
+
"This field cannot be null. Leave it out if it is optional, or give it a value."
|
|
686
|
+
)
|
|
687
|
+
);
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
issues.push({
|
|
691
|
+
path,
|
|
692
|
+
message: schemaIssue.message,
|
|
693
|
+
code: schemaIssue.code,
|
|
694
|
+
severity: "invalid"
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
const stored = validateItemGroup(draft);
|
|
699
|
+
if (issues.length === 0 && stored.success) {
|
|
700
|
+
return { status: "complete", data: stored.data, issues };
|
|
701
|
+
}
|
|
702
|
+
if (issues.length === 0) {
|
|
703
|
+
for (const error of stored.success ? [] : stored.errors) {
|
|
704
|
+
issues.push({ ...error, severity: "invalid" });
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
const invalid = issues.some((found) => found.severity !== "incomplete");
|
|
708
|
+
return { status: invalid ? "invalid" : "incomplete", issues };
|
|
709
|
+
}
|
|
710
|
+
function checkGroupIdentity(draft) {
|
|
711
|
+
const issues = [];
|
|
712
|
+
if (draft.schemaVersion !== "1.0") {
|
|
713
|
+
issues.push(
|
|
714
|
+
issue(
|
|
715
|
+
"schema_version_invalid",
|
|
716
|
+
["schemaVersion"],
|
|
717
|
+
'The draft must have schemaVersion "1.0". A draft from createItemGroupDraft has it.'
|
|
718
|
+
)
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
if (draft.type !== "item-group") {
|
|
722
|
+
issues.push(issue("type_mismatch", ["type"], `The draft's type must be "item-group".`));
|
|
723
|
+
}
|
|
724
|
+
if (isMissingId(draft.id)) {
|
|
725
|
+
issues.push(issue("id_required", ["id"], "The group has no id."));
|
|
726
|
+
}
|
|
727
|
+
if (!isUnset(draft.shuffle) && draft.shuffle !== "none" && draft.shuffle !== "within-group") {
|
|
728
|
+
issues.push(
|
|
729
|
+
issue(
|
|
730
|
+
"ig_shuffle_invalid",
|
|
731
|
+
["shuffle"],
|
|
732
|
+
'Shuffling is either "none" or "within-group". Leave it out to keep the authored order.'
|
|
733
|
+
)
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
return issues;
|
|
737
|
+
}
|
|
738
|
+
var KINDS = /* @__PURE__ */ new Set(["text", "audio", "video", "image", "mixed"]);
|
|
739
|
+
var MEDIA_FOR_KIND = {
|
|
740
|
+
audio: ["audio"],
|
|
741
|
+
video: ["video", "embed"],
|
|
742
|
+
image: ["image"],
|
|
743
|
+
mixed: ["audio", "video", "image", "embed"]
|
|
744
|
+
};
|
|
745
|
+
function checkStimulus(stimulus) {
|
|
746
|
+
const issues = [];
|
|
747
|
+
const at = (...rest) => ["stimulus", ...rest];
|
|
748
|
+
if (isMissingId(stimulus.id)) {
|
|
749
|
+
issues.push(issue("ig_stimulus_id_required", at("id"), "The stimulus has no id."));
|
|
750
|
+
}
|
|
751
|
+
const kind = stimulus.kind;
|
|
752
|
+
if (isUnwritten(kind)) {
|
|
753
|
+
issues.push(
|
|
754
|
+
issue(
|
|
755
|
+
"ig_stimulus_kind_required",
|
|
756
|
+
at("kind"),
|
|
757
|
+
"Choose what the questions are about: a text, a recording, a video, an image, or a mix."
|
|
758
|
+
)
|
|
759
|
+
);
|
|
760
|
+
} else if (typeof kind !== "string" || !KINDS.has(kind)) {
|
|
761
|
+
if (typeof kind === "string") {
|
|
762
|
+
issues.push(
|
|
763
|
+
issue(
|
|
764
|
+
"ig_stimulus_kind_invalid",
|
|
765
|
+
at("kind"),
|
|
766
|
+
`"${kind}" is not a stimulus kind. Use text, audio, video, image or mixed.`
|
|
767
|
+
)
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
if (isRecord(stimulus.media)) {
|
|
771
|
+
issues.push(...checkMedia(stimulus.media, at("media")));
|
|
772
|
+
}
|
|
773
|
+
return issues;
|
|
774
|
+
}
|
|
775
|
+
const hasBody = typeof stimulus.body === "string" && stimulus.body.trim() !== "";
|
|
776
|
+
const htmlPresent = !isUnset(stimulus.bodyHtml);
|
|
777
|
+
if (!hasBody && (kind === "text" || kind === "mixed" || htmlPresent)) {
|
|
778
|
+
issues.push(
|
|
779
|
+
issue(
|
|
780
|
+
"ig_stimulus_body_required",
|
|
781
|
+
at("body"),
|
|
782
|
+
isUnwritten(stimulus.bodyHtml) ? "Write the passage." : "Write the passage as plain text too. The rich-text version is shown only where a sanitiser is supplied."
|
|
783
|
+
)
|
|
784
|
+
);
|
|
785
|
+
}
|
|
786
|
+
if (kind !== "text" && isUnset(stimulus.media)) {
|
|
787
|
+
issues.push(
|
|
788
|
+
issue("ig_stimulus_media_required", at("media"), "Add the recording, video or image.")
|
|
789
|
+
);
|
|
790
|
+
} else if (isRecord(stimulus.media)) {
|
|
791
|
+
const allowed = MEDIA_FOR_KIND[kind];
|
|
792
|
+
const mediaType = stimulus.media.type;
|
|
793
|
+
if (allowed !== void 0 && typeof mediaType === "string" && !allowed.includes(mediaType)) {
|
|
794
|
+
issues.push(
|
|
795
|
+
issue(
|
|
796
|
+
"ig_stimulus_media_kind",
|
|
797
|
+
at("media", "type"),
|
|
798
|
+
`A "${kind}" stimulus cannot carry ${mediaType} media. Use ${allowed.join(" or ")}.`
|
|
799
|
+
)
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
issues.push(...checkMedia(stimulus.media, at("media")));
|
|
803
|
+
}
|
|
804
|
+
return issues;
|
|
805
|
+
}
|
|
806
|
+
function checkItems(draft) {
|
|
807
|
+
const issues = [];
|
|
808
|
+
const items = isUnset(draft.items) ? [] : draft.items;
|
|
809
|
+
if (!Array.isArray(items)) {
|
|
810
|
+
return issues;
|
|
811
|
+
}
|
|
812
|
+
if (items.length === 0) {
|
|
813
|
+
issues.push(
|
|
814
|
+
issue("ig_items_required", ["items"], "Add at least one question about this material.")
|
|
815
|
+
);
|
|
816
|
+
return issues;
|
|
817
|
+
}
|
|
818
|
+
const seen = /* @__PURE__ */ new Set();
|
|
819
|
+
const repeated = /* @__PURE__ */ new Set();
|
|
820
|
+
for (const [index, item] of items.entries()) {
|
|
821
|
+
if (!isRecord(item)) {
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
const ordinal = index + 1;
|
|
825
|
+
if (isMissingId(item.id)) {
|
|
826
|
+
issues.push(
|
|
827
|
+
issue("ig_item_id_required", ["items", index, "id"], `Question ${ordinal} has no id.`)
|
|
828
|
+
);
|
|
829
|
+
} else if (typeof item.id === "string") {
|
|
830
|
+
if (seen.has(item.id)) {
|
|
831
|
+
repeated.add(item.id);
|
|
832
|
+
}
|
|
833
|
+
seen.add(item.id);
|
|
834
|
+
}
|
|
835
|
+
if (item.type === "item-group") {
|
|
836
|
+
issues.push(
|
|
837
|
+
issue(
|
|
838
|
+
"ig_item_nested_group",
|
|
839
|
+
["items", index, "type"],
|
|
840
|
+
"Groups do not nest: every item must be an activity."
|
|
841
|
+
)
|
|
842
|
+
);
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
if (isUnwritten(item.type)) {
|
|
846
|
+
issues.push(
|
|
847
|
+
issue(
|
|
848
|
+
"ig_item_type_required",
|
|
849
|
+
["items", index, "type"],
|
|
850
|
+
`Choose what kind of question ${ordinal} is.`
|
|
851
|
+
)
|
|
852
|
+
);
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
855
|
+
if (typeof item.type !== "string") {
|
|
856
|
+
continue;
|
|
857
|
+
}
|
|
858
|
+
let result;
|
|
859
|
+
try {
|
|
860
|
+
result = validateDraft(item.type, item);
|
|
861
|
+
} catch {
|
|
862
|
+
issues.push(
|
|
863
|
+
issue(
|
|
864
|
+
"ig_item_type_unknown",
|
|
865
|
+
["items", index, "type"],
|
|
866
|
+
`"${item.type}" is not a registered activity type.`
|
|
867
|
+
)
|
|
868
|
+
);
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
for (const found of result.issues) {
|
|
872
|
+
issues.push({ ...found, path: ["items", String(index), ...found.path] });
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
for (const id of repeated) {
|
|
876
|
+
issues.push(
|
|
877
|
+
issue("ig_item_id_duplicate", ["items"], `More than one question has the id "${id}".`)
|
|
878
|
+
);
|
|
879
|
+
}
|
|
880
|
+
return issues;
|
|
881
|
+
}
|
|
882
|
+
|
|
589
883
|
// src/authoring/index.ts
|
|
590
884
|
function validateDraft(type, draft) {
|
|
885
|
+
return withValidationScope(() => validateDraftInScope(type, draft));
|
|
886
|
+
}
|
|
887
|
+
function validateDraftInScope(type, draft) {
|
|
591
888
|
const descriptor = getActivityTypeDescriptor(type);
|
|
592
889
|
if (descriptor === void 0) {
|
|
593
890
|
throw new UnknownActivityTypeError(String(type));
|
|
@@ -596,10 +893,11 @@ function validateDraft(type, draft) {
|
|
|
596
893
|
const issues = [...reported];
|
|
597
894
|
const parsed = descriptor.schema.safeParse(draft, { reportInput: true });
|
|
598
895
|
if (!parsed.success) {
|
|
896
|
+
const covered = coveredPathsOf(reported);
|
|
599
897
|
const reportedEmpty = /* @__PURE__ */ new Set();
|
|
600
898
|
for (const schemaIssue of parsed.error.issues) {
|
|
601
899
|
const path = schemaIssue.path.map(String);
|
|
602
|
-
if (
|
|
900
|
+
if (covered.has(pathKey(path))) {
|
|
603
901
|
continue;
|
|
604
902
|
}
|
|
605
903
|
if (path.length > 0 && refusesEmpty(draft, schemaIssue)) {
|
|
@@ -665,12 +963,6 @@ function normalise(found) {
|
|
|
665
963
|
severity: documented ?? (found.severity === "incomplete" ? "incomplete" : "invalid")
|
|
666
964
|
};
|
|
667
965
|
}
|
|
668
|
-
function covers(reported, failed) {
|
|
669
|
-
if (failed.length === 0) {
|
|
670
|
-
return reported.length === 0;
|
|
671
|
-
}
|
|
672
|
-
return failed.every((segment, index) => reported[index] === segment);
|
|
673
|
-
}
|
|
674
966
|
|
|
675
967
|
// src/redact.ts
|
|
676
968
|
function mergePolicy(base, overrides) {
|
|
@@ -891,7 +1183,16 @@ export {
|
|
|
891
1183
|
ActivitySchemaError,
|
|
892
1184
|
BlankConfigSchema,
|
|
893
1185
|
DEFAULT_PASS_THRESHOLD,
|
|
1186
|
+
DICTATION_MAX_ACCEPTED_TRANSCRIPTS,
|
|
1187
|
+
DICTATION_MAX_EQUIVALENCES,
|
|
1188
|
+
DICTATION_MAX_EQUIVALENCE_LENGTH,
|
|
1189
|
+
DICTATION_MAX_TEXT_LENGTH,
|
|
1190
|
+
DICTATION_MAX_TRANSCRIPT_LENGTH,
|
|
894
1191
|
DeferredScoringError,
|
|
1192
|
+
DictationDataSchema,
|
|
1193
|
+
DictationEquivalenceSchema,
|
|
1194
|
+
DictationSlowMediaSchema,
|
|
1195
|
+
DictationToleranceSchema,
|
|
895
1196
|
FeedbackSchema,
|
|
896
1197
|
FillInTheBlanksDataSchema,
|
|
897
1198
|
GapSelectBankSchema,
|
|
@@ -907,6 +1208,8 @@ export {
|
|
|
907
1208
|
MultipleChoiceOptionSchema,
|
|
908
1209
|
NativeControlHintSchema,
|
|
909
1210
|
RedactedBlankConfigSchema,
|
|
1211
|
+
RedactedDictationDataSchema,
|
|
1212
|
+
RedactedDictationSlowMediaSchema,
|
|
910
1213
|
RedactedFillInTheBlanksDataSchema,
|
|
911
1214
|
RedactedGapSelectBankSchema,
|
|
912
1215
|
RedactedGapSelectChoiceSchema,
|
|
@@ -928,6 +1231,7 @@ export {
|
|
|
928
1231
|
WrittenResponseRubricSchema,
|
|
929
1232
|
XAPIVerb,
|
|
930
1233
|
XAPI_VERB_DISPLAY,
|
|
1234
|
+
alignDictation,
|
|
931
1235
|
assertRedacted,
|
|
932
1236
|
assertRedactedItemGroup,
|
|
933
1237
|
canonicalJson,
|
|
@@ -937,7 +1241,12 @@ export {
|
|
|
937
1241
|
contentHash,
|
|
938
1242
|
countWords,
|
|
939
1243
|
createDraft,
|
|
1244
|
+
createItemGroupDraft,
|
|
940
1245
|
defineActivityType,
|
|
1246
|
+
dictationJsonSchema,
|
|
1247
|
+
dictationReferenceWords,
|
|
1248
|
+
dictationType,
|
|
1249
|
+
diffDictationChars,
|
|
941
1250
|
diffResponses,
|
|
942
1251
|
entryKeyOf,
|
|
943
1252
|
evaluate,
|
|
@@ -946,6 +1255,7 @@ export {
|
|
|
946
1255
|
fingerprint,
|
|
947
1256
|
flattenSequence,
|
|
948
1257
|
gapSelectJsonSchema,
|
|
1258
|
+
gapSelectType,
|
|
949
1259
|
getActivityTypeDescriptor,
|
|
950
1260
|
gradeFromRubric,
|
|
951
1261
|
gte,
|
|
@@ -980,6 +1290,7 @@ export {
|
|
|
980
1290
|
validateActivity,
|
|
981
1291
|
validateDraft,
|
|
982
1292
|
validateItemGroup,
|
|
1293
|
+
validateItemGroupDraft,
|
|
983
1294
|
validateXAPIStatement,
|
|
984
1295
|
verifyAttemptPlan,
|
|
985
1296
|
writtenResponseJsonSchema,
|