@intellectif/lk-core 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{activity-zdcAMtFB.d.cts → activity-wkzRemHx.d.cts} +113 -1
- package/dist/{activity-zdcAMtFB.d.ts → activity-wkzRemHx.d.ts} +113 -1
- package/dist/{chunk-QOAORTA4.cjs → chunk-4JR3UXX6.cjs} +51 -2
- package/dist/chunk-4JR3UXX6.cjs.map +1 -0
- package/dist/chunk-DUQVGLQ3.js +333 -0
- package/dist/chunk-DUQVGLQ3.js.map +1 -0
- package/dist/chunk-FCM5VJBS.cjs +187 -0
- package/dist/chunk-FCM5VJBS.cjs.map +1 -0
- package/dist/chunk-LSHDNA2T.js +187 -0
- package/dist/chunk-LSHDNA2T.js.map +1 -0
- package/dist/{chunk-5GJJHGY5.js → chunk-NUCEUU4P.js} +50 -1
- package/dist/chunk-NUCEUU4P.js.map +1 -0
- package/dist/{chunk-RUGIOQZY.cjs → chunk-R7PDJBRX.cjs} +29 -6
- package/dist/chunk-R7PDJBRX.cjs.map +1 -0
- package/dist/chunk-SM5HUGYU.cjs +333 -0
- package/dist/chunk-SM5HUGYU.cjs.map +1 -0
- package/dist/{chunk-HS7BYCGE.js → chunk-WV5WQ3SZ.js} +29 -6
- package/dist/chunk-WV5WQ3SZ.js.map +1 -0
- package/dist/index-BKyZrd94.d.cts +732 -0
- package/dist/index-CLmXzBhB.d.ts +732 -0
- package/dist/index.cjs +206 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +182 -6
- package/dist/index.d.ts +182 -6
- package/dist/index.js +203 -12
- package/dist/index.js.map +1 -1
- package/dist/schemas.cjs +17 -4
- package/dist/schemas.cjs.map +1 -1
- package/dist/schemas.d.cts +3 -460
- package/dist/schemas.d.ts +3 -460
- package/dist/schemas.js +16 -3
- package/dist/scoring.cjs +11 -4
- package/dist/scoring.cjs.map +1 -1
- package/dist/scoring.d.cts +199 -4
- package/dist/scoring.d.ts +199 -4
- package/dist/scoring.js +10 -3
- package/dist/xapi.cjs +5 -3
- package/dist/xapi.cjs.map +1 -1
- package/dist/xapi.d.cts +26 -2
- package/dist/xapi.d.ts +26 -2
- package/dist/xapi.js +6 -4
- package/package.json +38 -15
- package/dist/chunk-3YAVDV5F.js +0 -47
- package/dist/chunk-3YAVDV5F.js.map +0 -1
- package/dist/chunk-55O4M45K.js +0 -57
- package/dist/chunk-55O4M45K.js.map +0 -1
- package/dist/chunk-5GJJHGY5.js.map +0 -1
- package/dist/chunk-HS7BYCGE.js.map +0 -1
- package/dist/chunk-PIMX4B4D.cjs +0 -47
- package/dist/chunk-PIMX4B4D.cjs.map +0 -1
- package/dist/chunk-QOAORTA4.cjs.map +0 -1
- package/dist/chunk-QSVBYTNM.js +0 -94
- package/dist/chunk-QSVBYTNM.js.map +0 -1
- package/dist/chunk-RUGIOQZY.cjs.map +0 -1
- package/dist/chunk-THSZMZND.cjs +0 -94
- package/dist/chunk-THSZMZND.cjs.map +0 -1
- package/dist/chunk-YNTYWHZI.cjs +0 -57
- package/dist/chunk-YNTYWHZI.cjs.map +0 -1
|
@@ -63,6 +63,104 @@ declare function levenshteinDistance(a: string, b: string, max: number): number;
|
|
|
63
63
|
*/
|
|
64
64
|
declare function matchText(input: string, accepted: string | readonly string[], policy?: TextMatchPolicy): TextMatchResult;
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* The return trip for deferred grading.
|
|
68
|
+
*
|
|
69
|
+
* `evaluate()` can say a submission is `deferred` — graded later by an AI or a
|
|
70
|
+
* human — but until now there was no type for the grade that comes BACK, so
|
|
71
|
+
* every consumer invented one. The shapes here are the intersection of two
|
|
72
|
+
* independent production graders (a rubric-based essay grader and a CEFR
|
|
73
|
+
* speaking grader) that converged on the same envelope: a normalised total,
|
|
74
|
+
* per-criterion scores with comments, grader-authored artefacts, a confidence
|
|
75
|
+
* signal, an explicit human-review flag, and provenance.
|
|
76
|
+
*
|
|
77
|
+
* The SDK models the SHAPE and the arithmetic. It never calls a model, never
|
|
78
|
+
* holds a key, and never decides what a rubric means — that stays yours.
|
|
79
|
+
*/
|
|
80
|
+
/**
|
|
81
|
+
* Lifecycle of a deferred grade. A grade exists only in the `graded` state;
|
|
82
|
+
* every other state means "no grade, and here is why" — which is precisely the
|
|
83
|
+
* distinction that stops an ungraded submission being rendered as a zero.
|
|
84
|
+
*/
|
|
85
|
+
type GradingState = 'queued' | 'running' | 'graded' | 'failed' | 'skipped';
|
|
86
|
+
/** Who produced a grade. */
|
|
87
|
+
type GraderKind = 'ai' | 'human' | 'auto';
|
|
88
|
+
/** Provenance of a grade, so a re-grade two years later is explicable. */
|
|
89
|
+
interface Grader {
|
|
90
|
+
kind: GraderKind;
|
|
91
|
+
/** Identifier of the human grader, when `kind` is `'human'`. */
|
|
92
|
+
id?: string;
|
|
93
|
+
/** Model identifier, when `kind` is `'ai'` (e.g. a model name and version). */
|
|
94
|
+
model?: string;
|
|
95
|
+
/** Hash or id of the prompt/rubric revision used, for auditability. */
|
|
96
|
+
promptHash?: string;
|
|
97
|
+
}
|
|
98
|
+
/** Optional cost/usage telemetry emitted by an AI grader. */
|
|
99
|
+
interface GraderUsage {
|
|
100
|
+
promptTokens?: number;
|
|
101
|
+
completionTokens?: number;
|
|
102
|
+
costUsd?: number;
|
|
103
|
+
}
|
|
104
|
+
/** A grader's verdict on one rubric criterion. */
|
|
105
|
+
interface CriterionScore {
|
|
106
|
+
/** Matches a `WrittenResponseRubricCriterion.name` when a rubric is known. */
|
|
107
|
+
name: string;
|
|
108
|
+
/** Scaled [0,1]. Absent when the criterion is judged on an ordinal `band`. */
|
|
109
|
+
score?: number;
|
|
110
|
+
/** Ordinal verdict when the rubric is banded rather than numeric (e.g. `"B1"`). */
|
|
111
|
+
band?: string;
|
|
112
|
+
/** The grader's comment for this criterion, addressed to the learner. */
|
|
113
|
+
comment?: string;
|
|
114
|
+
/** Weight actually applied, echoed from the rubric so the total is checkable. */
|
|
115
|
+
weight?: number;
|
|
116
|
+
/** Not applicable to this submission (e.g. interaction on a monologue task). */
|
|
117
|
+
notApplicable?: boolean;
|
|
118
|
+
}
|
|
119
|
+
/** A suggested correction anchored in the learner's own text. */
|
|
120
|
+
interface InlineCorrection {
|
|
121
|
+
/** Verbatim span from the submission. */
|
|
122
|
+
original: string;
|
|
123
|
+
/** Suggested replacement. */
|
|
124
|
+
corrected: string;
|
|
125
|
+
explanation?: string;
|
|
126
|
+
/** Character offsets into the submitted text, so a UI can anchor it inline. */
|
|
127
|
+
range?: {
|
|
128
|
+
start: number;
|
|
129
|
+
end: number;
|
|
130
|
+
};
|
|
131
|
+
/** Error-type tag (`"article"`, `"tense"`, `"register"`). */
|
|
132
|
+
category?: string;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* A grade that came back from an asynchronous grader. `score` is scaled
|
|
136
|
+
* [0,1] against `maxScore`, matching every other score in the SDK, so a
|
|
137
|
+
* grader that works in points must normalise before handing one over
|
|
138
|
+
* (or set `maxScore` accordingly).
|
|
139
|
+
*/
|
|
140
|
+
interface GradeRecord {
|
|
141
|
+
score: number;
|
|
142
|
+
maxScore: number;
|
|
143
|
+
passed: boolean;
|
|
144
|
+
/** Narrative feedback addressed to the learner. */
|
|
145
|
+
feedback: string | null;
|
|
146
|
+
/** Per-criterion breakdown, when the grader worked against a rubric. */
|
|
147
|
+
criteria?: CriterionScore[];
|
|
148
|
+
/** Corrections anchored in the learner's text. */
|
|
149
|
+
corrections?: InlineCorrection[];
|
|
150
|
+
/** Supporting observations the grader cited. */
|
|
151
|
+
evidence?: string[];
|
|
152
|
+
/** How the grader reached this verdict. */
|
|
153
|
+
rationale?: string;
|
|
154
|
+
/** The grader's own confidence — a signal for routing to human review. */
|
|
155
|
+
confidence?: 'high' | 'medium' | 'low';
|
|
156
|
+
/** The grader is unsure or the submission is atypical; route to a human. */
|
|
157
|
+
requiresHumanReview?: boolean;
|
|
158
|
+
grader?: Grader;
|
|
159
|
+
usage?: GraderUsage;
|
|
160
|
+
/** ISO 8601 timestamp of when the grade was produced. */
|
|
161
|
+
gradedAt?: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
66
164
|
/**
|
|
67
165
|
* An xAPI Activity object — the thing a statement is about.
|
|
68
166
|
* This SDK only builds Activity-type objects (xAPI 1.0.3 §4.1.4.1).
|
|
@@ -564,6 +662,20 @@ type ItemOutcome = {
|
|
|
564
662
|
maxScore: number;
|
|
565
663
|
/** Synchronously computable progress facts (word bounds, counts). */
|
|
566
664
|
partial?: DeferredScoringPartial;
|
|
665
|
+
} | {
|
|
666
|
+
status: 'graded';
|
|
667
|
+
/**
|
|
668
|
+
* A grade produced by an asynchronous grader (AI or human) and handed
|
|
669
|
+
* back to the SDK. This is the state a `deferred` outcome transitions
|
|
670
|
+
* to once grading completes; nothing in the SDK ever manufactures it.
|
|
671
|
+
*/
|
|
672
|
+
grade: GradeRecord;
|
|
673
|
+
/** Scaled score in the range [0, 1], mirrored from `grade` for uniform reads. */
|
|
674
|
+
score: number;
|
|
675
|
+
maxScore: number;
|
|
676
|
+
passed: boolean;
|
|
677
|
+
/** Narrative feedback from the grader, mirrored from `grade`. */
|
|
678
|
+
feedback: string | null;
|
|
567
679
|
} | {
|
|
568
680
|
status: 'unscorable';
|
|
569
681
|
/** Why no grade can be produced (e.g. unregistered activity type). */
|
|
@@ -623,4 +735,4 @@ interface InteractionEvent {
|
|
|
623
735
|
payload: Record<string, unknown>;
|
|
624
736
|
}
|
|
625
737
|
|
|
626
|
-
export { type ActivityData as A, type BlankConfig as B, type
|
|
738
|
+
export { type ActivityData as A, type BlankConfig as B, type CriterionScore as C, type DeferredScoringPartial as D, type XAPIError as E, type FillInTheBlanksData as F, type GradeRecord as G, type XAPIObject as H, type InlineCorrection as I, type XAPIResult as J, type XAPIScore as K, type LearnerResponse as L, type MultipleChoiceData as M, type XAPIStatement as N, type XAPIVerbObject as O, levenshteinDistance as P, matchText as Q, type ScoringDetail as S, type TextMatchPolicy as T, type ValidationError as V, type WrittenResponseData as W, type XAPIActor as X, type ActivityDataMap as a, type ActivityFeedback as b, type ActivityMedia as c, type ActivityResult as d, type ActivityType as e, type FillInTheBlanksLearnerResponse as f, type Grader as g, type GraderKind as h, type GraderUsage as i, type GradingState as j, type InteractionEvent as k, type InteractionKind as l, type ItemOutcome as m, type LearnerResponseMap as n, type MultipleChoiceLearnerResponse as o, type MultipleChoiceOption as p, type ScoringOutcome as q, type ScoringResult as r, type TextMatchResult as s, type ValidationResult as t, type WrittenResponseLearnerResponse as u, type WrittenResponseRubric as v, type WrittenResponseRubricCriterion as w, type XAPIConfig as x, type XAPIContext as y, type XAPIContextActivities as z };
|
|
@@ -63,6 +63,104 @@ declare function levenshteinDistance(a: string, b: string, max: number): number;
|
|
|
63
63
|
*/
|
|
64
64
|
declare function matchText(input: string, accepted: string | readonly string[], policy?: TextMatchPolicy): TextMatchResult;
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* The return trip for deferred grading.
|
|
68
|
+
*
|
|
69
|
+
* `evaluate()` can say a submission is `deferred` — graded later by an AI or a
|
|
70
|
+
* human — but until now there was no type for the grade that comes BACK, so
|
|
71
|
+
* every consumer invented one. The shapes here are the intersection of two
|
|
72
|
+
* independent production graders (a rubric-based essay grader and a CEFR
|
|
73
|
+
* speaking grader) that converged on the same envelope: a normalised total,
|
|
74
|
+
* per-criterion scores with comments, grader-authored artefacts, a confidence
|
|
75
|
+
* signal, an explicit human-review flag, and provenance.
|
|
76
|
+
*
|
|
77
|
+
* The SDK models the SHAPE and the arithmetic. It never calls a model, never
|
|
78
|
+
* holds a key, and never decides what a rubric means — that stays yours.
|
|
79
|
+
*/
|
|
80
|
+
/**
|
|
81
|
+
* Lifecycle of a deferred grade. A grade exists only in the `graded` state;
|
|
82
|
+
* every other state means "no grade, and here is why" — which is precisely the
|
|
83
|
+
* distinction that stops an ungraded submission being rendered as a zero.
|
|
84
|
+
*/
|
|
85
|
+
type GradingState = 'queued' | 'running' | 'graded' | 'failed' | 'skipped';
|
|
86
|
+
/** Who produced a grade. */
|
|
87
|
+
type GraderKind = 'ai' | 'human' | 'auto';
|
|
88
|
+
/** Provenance of a grade, so a re-grade two years later is explicable. */
|
|
89
|
+
interface Grader {
|
|
90
|
+
kind: GraderKind;
|
|
91
|
+
/** Identifier of the human grader, when `kind` is `'human'`. */
|
|
92
|
+
id?: string;
|
|
93
|
+
/** Model identifier, when `kind` is `'ai'` (e.g. a model name and version). */
|
|
94
|
+
model?: string;
|
|
95
|
+
/** Hash or id of the prompt/rubric revision used, for auditability. */
|
|
96
|
+
promptHash?: string;
|
|
97
|
+
}
|
|
98
|
+
/** Optional cost/usage telemetry emitted by an AI grader. */
|
|
99
|
+
interface GraderUsage {
|
|
100
|
+
promptTokens?: number;
|
|
101
|
+
completionTokens?: number;
|
|
102
|
+
costUsd?: number;
|
|
103
|
+
}
|
|
104
|
+
/** A grader's verdict on one rubric criterion. */
|
|
105
|
+
interface CriterionScore {
|
|
106
|
+
/** Matches a `WrittenResponseRubricCriterion.name` when a rubric is known. */
|
|
107
|
+
name: string;
|
|
108
|
+
/** Scaled [0,1]. Absent when the criterion is judged on an ordinal `band`. */
|
|
109
|
+
score?: number;
|
|
110
|
+
/** Ordinal verdict when the rubric is banded rather than numeric (e.g. `"B1"`). */
|
|
111
|
+
band?: string;
|
|
112
|
+
/** The grader's comment for this criterion, addressed to the learner. */
|
|
113
|
+
comment?: string;
|
|
114
|
+
/** Weight actually applied, echoed from the rubric so the total is checkable. */
|
|
115
|
+
weight?: number;
|
|
116
|
+
/** Not applicable to this submission (e.g. interaction on a monologue task). */
|
|
117
|
+
notApplicable?: boolean;
|
|
118
|
+
}
|
|
119
|
+
/** A suggested correction anchored in the learner's own text. */
|
|
120
|
+
interface InlineCorrection {
|
|
121
|
+
/** Verbatim span from the submission. */
|
|
122
|
+
original: string;
|
|
123
|
+
/** Suggested replacement. */
|
|
124
|
+
corrected: string;
|
|
125
|
+
explanation?: string;
|
|
126
|
+
/** Character offsets into the submitted text, so a UI can anchor it inline. */
|
|
127
|
+
range?: {
|
|
128
|
+
start: number;
|
|
129
|
+
end: number;
|
|
130
|
+
};
|
|
131
|
+
/** Error-type tag (`"article"`, `"tense"`, `"register"`). */
|
|
132
|
+
category?: string;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* A grade that came back from an asynchronous grader. `score` is scaled
|
|
136
|
+
* [0,1] against `maxScore`, matching every other score in the SDK, so a
|
|
137
|
+
* grader that works in points must normalise before handing one over
|
|
138
|
+
* (or set `maxScore` accordingly).
|
|
139
|
+
*/
|
|
140
|
+
interface GradeRecord {
|
|
141
|
+
score: number;
|
|
142
|
+
maxScore: number;
|
|
143
|
+
passed: boolean;
|
|
144
|
+
/** Narrative feedback addressed to the learner. */
|
|
145
|
+
feedback: string | null;
|
|
146
|
+
/** Per-criterion breakdown, when the grader worked against a rubric. */
|
|
147
|
+
criteria?: CriterionScore[];
|
|
148
|
+
/** Corrections anchored in the learner's text. */
|
|
149
|
+
corrections?: InlineCorrection[];
|
|
150
|
+
/** Supporting observations the grader cited. */
|
|
151
|
+
evidence?: string[];
|
|
152
|
+
/** How the grader reached this verdict. */
|
|
153
|
+
rationale?: string;
|
|
154
|
+
/** The grader's own confidence — a signal for routing to human review. */
|
|
155
|
+
confidence?: 'high' | 'medium' | 'low';
|
|
156
|
+
/** The grader is unsure or the submission is atypical; route to a human. */
|
|
157
|
+
requiresHumanReview?: boolean;
|
|
158
|
+
grader?: Grader;
|
|
159
|
+
usage?: GraderUsage;
|
|
160
|
+
/** ISO 8601 timestamp of when the grade was produced. */
|
|
161
|
+
gradedAt?: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
66
164
|
/**
|
|
67
165
|
* An xAPI Activity object — the thing a statement is about.
|
|
68
166
|
* This SDK only builds Activity-type objects (xAPI 1.0.3 §4.1.4.1).
|
|
@@ -564,6 +662,20 @@ type ItemOutcome = {
|
|
|
564
662
|
maxScore: number;
|
|
565
663
|
/** Synchronously computable progress facts (word bounds, counts). */
|
|
566
664
|
partial?: DeferredScoringPartial;
|
|
665
|
+
} | {
|
|
666
|
+
status: 'graded';
|
|
667
|
+
/**
|
|
668
|
+
* A grade produced by an asynchronous grader (AI or human) and handed
|
|
669
|
+
* back to the SDK. This is the state a `deferred` outcome transitions
|
|
670
|
+
* to once grading completes; nothing in the SDK ever manufactures it.
|
|
671
|
+
*/
|
|
672
|
+
grade: GradeRecord;
|
|
673
|
+
/** Scaled score in the range [0, 1], mirrored from `grade` for uniform reads. */
|
|
674
|
+
score: number;
|
|
675
|
+
maxScore: number;
|
|
676
|
+
passed: boolean;
|
|
677
|
+
/** Narrative feedback from the grader, mirrored from `grade`. */
|
|
678
|
+
feedback: string | null;
|
|
567
679
|
} | {
|
|
568
680
|
status: 'unscorable';
|
|
569
681
|
/** Why no grade can be produced (e.g. unregistered activity type). */
|
|
@@ -623,4 +735,4 @@ interface InteractionEvent {
|
|
|
623
735
|
payload: Record<string, unknown>;
|
|
624
736
|
}
|
|
625
737
|
|
|
626
|
-
export { type ActivityData as A, type BlankConfig as B, type
|
|
738
|
+
export { type ActivityData as A, type BlankConfig as B, type CriterionScore as C, type DeferredScoringPartial as D, type XAPIError as E, type FillInTheBlanksData as F, type GradeRecord as G, type XAPIObject as H, type InlineCorrection as I, type XAPIResult as J, type XAPIScore as K, type LearnerResponse as L, type MultipleChoiceData as M, type XAPIStatement as N, type XAPIVerbObject as O, levenshteinDistance as P, matchText as Q, type ScoringDetail as S, type TextMatchPolicy as T, type ValidationError as V, type WrittenResponseData as W, type XAPIActor as X, type ActivityDataMap as a, type ActivityFeedback as b, type ActivityMedia as c, type ActivityResult as d, type ActivityType as e, type FillInTheBlanksLearnerResponse as f, type Grader as g, type GraderKind as h, type GraderUsage as i, type GradingState as j, type InteractionEvent as k, type InteractionKind as l, type ItemOutcome as m, type LearnerResponseMap as n, type MultipleChoiceLearnerResponse as o, type MultipleChoiceOption as p, type ScoringOutcome as q, type ScoringResult as r, type TextMatchResult as s, type ValidationResult as t, type WrittenResponseLearnerResponse as u, type WrittenResponseRubric as v, type WrittenResponseRubricCriterion as w, type XAPIConfig as x, type XAPIContext as y, type XAPIContextActivities as z };
|
|
@@ -10,6 +10,46 @@ function countWords(text) {
|
|
|
10
10
|
return trimmed.split(/\s+/).length;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
// src/errors.ts
|
|
14
|
+
var ActivitySchemaError = class extends Error {
|
|
15
|
+
constructor(activityType, errors) {
|
|
16
|
+
super(`Invalid activity data for type "${activityType}"`);
|
|
17
|
+
this.activityType = activityType;
|
|
18
|
+
this.errors = errors;
|
|
19
|
+
this.name = "ActivitySchemaError";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
};
|
|
24
|
+
var UnknownActivityTypeError = class extends Error {
|
|
25
|
+
constructor(activityType) {
|
|
26
|
+
super(`Activity type "${activityType}" is not registered`);
|
|
27
|
+
this.activityType = activityType;
|
|
28
|
+
this.name = "UnknownActivityTypeError";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
};
|
|
32
|
+
var RedactedScoringError = class extends Error {
|
|
33
|
+
constructor(activityType) {
|
|
34
|
+
super(
|
|
35
|
+
`Activity data for "${activityType}" carries no answer key (it looks redacted), so it cannot be scored. Score against the full activity data server-side, or use evaluate() which returns { status: "unscorable" }.`
|
|
36
|
+
);
|
|
37
|
+
this.activityType = activityType;
|
|
38
|
+
this.name = "RedactedScoringError";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
};
|
|
42
|
+
var DeferredScoringError = class extends Error {
|
|
43
|
+
constructor(activityType) {
|
|
44
|
+
super(
|
|
45
|
+
`Activity type "${activityType}" is graded asynchronously and has no synchronous score. Use evaluate() \u2014 it returns { status: 'deferred' } for this type.`
|
|
46
|
+
);
|
|
47
|
+
this.activityType = activityType;
|
|
48
|
+
this.name = "DeferredScoringError";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
};
|
|
52
|
+
|
|
13
53
|
// src/schemas/feedback.ts
|
|
14
54
|
var _v4 = require('zod/v4');
|
|
15
55
|
var FeedbackSchema = _v4.z.looseObject({
|
|
@@ -316,6 +356,11 @@ function defineActivityType(descriptor) {
|
|
|
316
356
|
return descriptor;
|
|
317
357
|
}
|
|
318
358
|
function registerActivityType(descriptor) {
|
|
359
|
+
if (descriptor.type === "item-group") {
|
|
360
|
+
throw new Error(
|
|
361
|
+
`"item-group" is reserved for the SDK's item-group container (see ItemGroup) and cannot be registered as an activity type.`
|
|
362
|
+
);
|
|
363
|
+
}
|
|
319
364
|
const existing = registry.get(descriptor.type);
|
|
320
365
|
if (existing !== void 0) {
|
|
321
366
|
if (existing === descriptor) {
|
|
@@ -572,5 +617,9 @@ registerActivityType(writtenResponseType);
|
|
|
572
617
|
|
|
573
618
|
|
|
574
619
|
|
|
575
|
-
|
|
576
|
-
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
exports.countWords = countWords; exports.ActivitySchemaError = ActivitySchemaError; exports.UnknownActivityTypeError = UnknownActivityTypeError; exports.RedactedScoringError = RedactedScoringError; exports.DeferredScoringError = DeferredScoringError; exports.FeedbackSchema = FeedbackSchema; exports.MediaUrlSchema = MediaUrlSchema; exports.MediaSchema = MediaSchema; exports.TextMatchPolicySchema = TextMatchPolicySchema; exports.BlankConfigSchema = BlankConfigSchema; exports.FillInTheBlanksDataSchema = FillInTheBlanksDataSchema; exports.MultipleChoiceOptionSchema = MultipleChoiceOptionSchema; exports.MultipleChoiceDataSchema = MultipleChoiceDataSchema; exports.WrittenResponseRubricCriterionSchema = WrittenResponseRubricCriterionSchema; exports.WrittenResponseRubricSchema = WrittenResponseRubricSchema; exports.WrittenResponseDataSchema = WrittenResponseDataSchema; exports.RedactedMultipleChoiceOptionSchema = RedactedMultipleChoiceOptionSchema; exports.RedactedMultipleChoiceDataSchema = RedactedMultipleChoiceDataSchema; exports.RedactedBlankConfigSchema = RedactedBlankConfigSchema; exports.RedactedFillInTheBlanksDataSchema = RedactedFillInTheBlanksDataSchema; exports.RedactedWrittenResponseDataSchema = RedactedWrittenResponseDataSchema; exports.levenshteinDistance = levenshteinDistance; exports.matchText = matchText; exports.defineActivityType = defineActivityType; exports.registerActivityType = registerActivityType; exports.getActivityTypeDescriptor = getActivityTypeDescriptor; exports.registeredActivityTypes = registeredActivityTypes; exports.multipleChoiceType = multipleChoiceType; exports.fillInTheBlanksType = fillInTheBlanksType; exports.writtenResponseType = writtenResponseType;
|
|
625
|
+
//# sourceMappingURL=chunk-4JR3UXX6.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/learning-kit/learning-kit/packages/lk-core/dist/chunk-4JR3UXX6.cjs","../src/count-words.ts","../src/errors.ts","../src/schemas/feedback.ts","../src/schemas/media.ts","../src/schemas/fill-in-the-blanks.ts","../src/schemas/multiple-choice.ts","../src/schemas/written-response.ts","../src/schemas/redacted.ts","../src/scoring/text-match.ts","../src/registry/registry.ts","../src/scoring/strategies/all-or-nothing.ts","../src/scoring/strategies/partial.ts","../src/scoring/activity-scorers/fill-in-the-blanks.ts","../src/scoring/activity-scorers/multiple-choice.ts","../src/registry/builtins.ts"],"names":["z"],"mappings":"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","file":"/home/runner/work/learning-kit/learning-kit/packages/lk-core/dist/chunk-4JR3UXX6.cjs","sourcesContent":[null,"/**\n * Canonical word counter for written-response bounds (Req 22.9): tokens are\n * maximal runs of non-whitespace (split on `\\s+`), so hyphenated forms\n * (`well-known`) count as one word. The empty / whitespace-only string counts\n * 0. Consumers must use this helper rather than re-implementing the split so\n * client previews, SDK scoring, and stored `wordCount` values always agree.\n */\nexport function countWords(text: string): number {\n // Tolerate non-strings: this runs on client-supplied submission payloads,\n // where a malformed value must score 0 rather than throw mid-grading.\n if (typeof text !== 'string') {\n return 0;\n }\n const trimmed = text.trim();\n if (trimmed === '') {\n return 0;\n }\n return trimmed.split(/\\s+/).length;\n}\n","import type { ValidationError } from './types/activity.js';\n\n/** Thrown when activity data fails schema validation at a component boundary. */\nexport class ActivitySchemaError extends Error {\n constructor(\n public readonly activityType: string,\n public readonly errors: ValidationError[],\n ) {\n super(`Invalid activity data for type \"${activityType}\"`);\n this.name = 'ActivitySchemaError';\n }\n}\n\n/** Thrown when an unrecognised activity type is passed to the scoring engine. */\nexport class UnknownActivityTypeError extends Error {\n constructor(public readonly activityType: string) {\n super(`Activity type \"${activityType}\" is not registered`);\n this.name = 'UnknownActivityTypeError';\n }\n}\n\n/**\n * Thrown when `score()` is asked to grade a `redact()` projection (or any\n * activity data whose answer key is missing, yielding a non-finite score).\n * Redacted data is learner-safe precisely because the key was removed, so a\n * score derived from it is meaningless — previously this produced a silent\n * `NaN` that serialized to `null` in a grade column. `evaluate()` returns\n * `{ status: 'unscorable' }` for the same input instead of throwing.\n */\nexport class RedactedScoringError extends Error {\n constructor(public readonly activityType: string) {\n super(\n `Activity data for \"${activityType}\" carries no answer key (it looks redacted), so it cannot be scored. ` +\n 'Score against the full activity data server-side, or use evaluate() which returns { status: \"unscorable\" }.',\n );\n this.name = 'RedactedScoringError';\n }\n}\n\n/**\n * Thrown when `score()` is called for an activity type whose grading is\n * deferred (asynchronous AI/human grading, e.g. `written-response`). A\n * deferred submission has no synchronous score — treating it as 0 would show\n * a learner a failing grade for work that simply has not been graded yet.\n * Call `evaluate()` instead, which returns `{ status: 'deferred', ... }`.\n */\nexport class DeferredScoringError extends Error {\n constructor(public readonly activityType: string) {\n super(\n `Activity type \"${activityType}\" is graded asynchronously and has no synchronous score. ` +\n `Use evaluate() — it returns { status: 'deferred' } for this type.`,\n );\n this.name = 'DeferredScoringError';\n }\n}\n","import { z } from 'zod/v4';\n\n/**\n * Optional authored \"overall feedback\" shown after submission, selected by\n * whether the learner passed (h5p-style overall feedback). Both fields are\n * optional; non-empty when present.\n */\nexport const FeedbackSchema = z.looseObject({\n correct: z.string().min(1).optional(),\n incorrect: z.string().min(1).optional(),\n});\n","import { z } from 'zod/v4';\n\n/**\n * Optional media attached to an activity, rendered above the question or\n * passage — e.g. a recording to listen to, or an embedded video to watch\n * before answering. URL-only by design: hosting/delivery (S3/CDN, or the\n * provider's own embed for `embed`) is the consuming app's responsibility\n * (see requirements \"Non-Goals and Shared Responsibility\").\n *\n * - `image`: rendered as `<img>` — `alt` is REQUIRED (WCAG 1.1.1 / Req 14.5).\n * - `audio` / `video`: rendered with native controls; `alt` is an optional\n * accessible label; `captionsUrl` points at a WebVTT `<track>`. `url` must\n * be a direct media file (NOT a YouTube/Vimeo page — use `embed` for those).\n * - `embed`: rendered as a sandboxed `<iframe>` for provider players\n * (YouTube/Vimeo/etc.). `url` MUST be the provider's *embeddable* URL\n * (e.g. `https://www.youtube.com/embed/<id>`). `alt` is REQUIRED and used\n * as the iframe's accessible `title` (WCAG 4.1.2 / 2.4.1).\n */\n/**\n * Media URL policy (security-reviewed): absolute URLs must use `https:`,\n * `http:`, `data:`, or `blob:`; root-relative paths (`/media/x.mp3`) are\n * allowed for same-origin hosting. Everything else — notably `javascript:`,\n * `file:`, `ftp:` — is rejected: these URLs land in `src` attributes\n * (including an iframe for `embed`), so an unvetted scheme is a stored-XSS\n * vector in every consuming app.\n */\nexport const MediaUrlSchema = z.union([\n z.url().refine((value) => /^(https?|data|blob):/i.test(value), {\n error: 'Absolute media URLs must use the https:, http:, data:, or blob: scheme.',\n }),\n z.string().regex(/^\\/(?!\\/)\\S*$/, {\n error: 'Relative media URLs must be root-relative (a single leading \"/\").',\n }),\n]);\n\nexport const MediaSchema = z\n .looseObject({\n type: z.enum(['image', 'audio', 'video', 'embed']),\n url: MediaUrlSchema,\n alt: z.string().min(1).optional(),\n captionsUrl: MediaUrlSchema.optional(),\n })\n .refine(\n (m) =>\n (m.type !== 'image' && m.type !== 'embed') || (typeof m.alt === 'string' && m.alt.length > 0),\n {\n error: 'image and embed media require non-empty alt text (WCAG 1.1.1 / 4.1.2).',\n path: ['alt'],\n },\n )\n .refine((m) => m.type !== 'embed' || /^https?:\\/\\//i.test(m.url), {\n error:\n 'embed media requires an absolute http(s) provider URL. data:, blob:, and relative URLs are not allowed for embeds — the embed iframe runs with allow-scripts, and a data:/same-origin document there is an XSS vector.',\n path: ['url'],\n });\n","import { z } from 'zod/v4';\nimport { FeedbackSchema } from './feedback.js';\nimport { MediaSchema } from './media.js';\n\n/** Matches `{{ blank_id }}` placeholders in a passage, capturing the trimmed id. */\nconst PLACEHOLDER_RE = /\\{\\{\\s*([^{}]+?)\\s*\\}\\}/g;\n\n/**\n * Zod schema for a `TextMatchPolicy` — the opt-in matching tolerances a blank\n * may declare. Every default reproduces the v1 trim + case-fold semantics.\n */\nexport const TextMatchPolicySchema = z.looseObject({\n caseSensitive: z.boolean().optional(),\n trim: z.boolean().optional(),\n normalize: z.enum(['none', 'NFC', 'NFKC']).optional(),\n foldDiacritics: z.boolean().optional(),\n collapseInnerWhitespace: z.boolean().optional(),\n ignorePunctuation: z.boolean().optional(),\n levenshtein: z.number().int().min(0).optional(),\n locale: z.string().optional(),\n});\n\n/**\n * Zod schema for a single fill-in-the-blank slot configuration. Loose:\n * unknown keys are preserved through validation. Accepted answers must\n * contain non-whitespace characters — a whitespace-only accepted answer\n * normalizes to the empty string and would mark an empty response correct.\n */\nexport const BlankConfigSchema = z.looseObject({\n id: z.string().min(1),\n acceptedAnswers: z\n .array(\n z\n .string()\n .min(1)\n .refine((answer) => answer.trim().length > 0, {\n error: 'Accepted answers must contain non-whitespace characters.',\n }),\n )\n .min(1),\n caseSensitive: z.boolean().optional(),\n trimWhitespace: z.boolean().optional(),\n match: TextMatchPolicySchema.optional(),\n hint: z.string().optional(),\n feedback: z.string().optional(),\n});\n\n/**\n * Zod schema validating the full Fill-in-the-Blanks activity data contract.\n * Loose at every level: unknown keys are preserved, never stripped (B7).\n *\n * The refinement enforces a true one-to-one correspondence between `{{id}}`\n * placeholders and `blanks[].id`: every blank id appears EXACTLY ONCE in the\n * passage and exactly once in `blanks[]`. (The previous set-based check let\n * duplicate placeholders and duplicate blank configs through, corrupting the\n * partial-score denominator and per-item details.)\n */\nexport const FillInTheBlanksDataSchema = z\n .looseObject({\n schemaVersion: z.literal('1.0'),\n type: z.literal('fill-in-the-blanks'),\n id: z.string().min(1),\n title: z.string().min(1),\n passage: z.string().min(1),\n passageHtml: z.string().optional(),\n blanks: z.array(BlankConfigSchema).min(1),\n scoringStrategy: z.enum(['all-or-nothing', 'partial']),\n media: MediaSchema.optional(),\n feedback: FeedbackSchema.optional(),\n passThreshold: z.number().min(0).max(1).optional(),\n locale: z.string().optional(),\n learningObjectives: z.array(z.string()).optional(),\n difficultyLevel: z.literal([1, 2, 3, 4, 5]).optional(),\n })\n .refine(\n (data) => {\n const placeholderCounts = new Map<string, number>();\n for (const match of data.passage.matchAll(PLACEHOLDER_RE)) {\n const id = match[1] as string;\n placeholderCounts.set(id, (placeholderCounts.get(id) ?? 0) + 1);\n }\n const blankIds = data.blanks.map((blank) => blank.id);\n if (new Set(blankIds).size !== blankIds.length) {\n return false;\n }\n if (placeholderCounts.size !== blankIds.length) {\n return false;\n }\n return blankIds.every((id) => placeholderCounts.get(id) === 1);\n },\n {\n error:\n 'Each blank id must appear exactly once in blanks[] and have exactly one matching {{id}} placeholder in the passage, and vice versa.',\n path: ['passage'],\n },\n );\n","import { z } from 'zod/v4';\nimport { FeedbackSchema } from './feedback.js';\nimport { MediaSchema } from './media.js';\n\n/**\n * Zod schema for a single Multiple Choice option. Loose: unknown keys are\n * preserved through validation (forward-compat / consumer sidecars — B7).\n */\nexport const MultipleChoiceOptionSchema = z.looseObject({\n id: z.string().min(1),\n text: z.string().min(1),\n isCorrect: z.boolean(),\n feedback: z.string().optional(),\n});\n\n/**\n * Zod schema validating the full Multiple Choice activity data contract.\n * Loose at every level: unknown keys are preserved, never stripped, so a\n * v0.3 runtime reading a future payload (or a consumer sidecar field) does\n * not silently delete data.\n *\n * Semantic guards: (1) at least one option must be correct, otherwise the\n * activity can never be answered correctly; (2) `mode: 'single'` must have\n * exactly one correct option — multiple correct options under single-select\n * make `showCorrectAnswers` and the xAPI correct-response ambiguous and mask\n * authoring errors; (3) option ids must be unique — the scorer looks options\n * up by id, so a duplicate id makes one option unscoreable. All are\n * unrepresentable in JSON Schema and are dropped from `toJSONSchema` output\n * by design.\n */\nexport const MultipleChoiceDataSchema = z\n .looseObject({\n schemaVersion: z.literal('1.0'),\n type: z.literal('multiple-choice'),\n id: z.string().min(1),\n title: z.string().min(1),\n question: z.string().min(1),\n questionHtml: z.string().optional(),\n mode: z.enum(['single', 'multi']),\n options: z.array(MultipleChoiceOptionSchema).min(2).max(26),\n scoringStrategy: z.enum(['all-or-nothing', 'partial']),\n media: MediaSchema.optional(),\n feedback: FeedbackSchema.optional(),\n passThreshold: z.number().min(0).max(1).optional(),\n shuffle: z.boolean().optional(),\n locale: z.string().optional(),\n learningObjectives: z.array(z.string()).optional(),\n difficultyLevel: z.literal([1, 2, 3, 4, 5]).optional(),\n })\n .refine((data) => data.options.some((option) => option.isCorrect), {\n error: 'At least one option must be marked correct.',\n path: ['options'],\n })\n .refine(\n (data) =>\n data.mode !== 'single' || data.options.filter((option) => option.isCorrect).length === 1,\n {\n error: 'Single-select activities (mode: \"single\") must have exactly one correct option.',\n path: ['options'],\n },\n )\n .refine((data) => new Set(data.options.map((option) => option.id)).size === data.options.length, {\n error: 'Option ids must be unique within the activity.',\n path: ['options'],\n });\n","import { z } from 'zod/v4';\nimport { FeedbackSchema } from './feedback.js';\nimport { MediaSchema } from './media.js';\n\n/** Zod schema for a single rubric criterion. Loose: unknown keys preserved. */\nexport const WrittenResponseRubricCriterionSchema = z.looseObject({\n name: z.string().min(1),\n description: z.string().optional(),\n weight: z.number().min(0),\n});\n\n/** Zod schema for a written-response grading rubric. Loose: unknown keys preserved. */\nexport const WrittenResponseRubricSchema = z.looseObject({\n label: z.string().optional(),\n criteria: z.array(WrittenResponseRubricCriterionSchema).min(1),\n});\n\n/**\n * Zod schema validating the Written Response activity data contract (Req 22).\n *\n * Wire-format constraints (Req 22.9): field names are locked for\n * byte-compatibility with consumer-stored rows, and the schema is loose at\n * EVERY level (Req 22.5) — unknown top-level keys, `promptHtml`, `rubric`\n * sidecars and any future fields survive `validateActivity` verbatim.\n */\nexport const WrittenResponseDataSchema = z\n .looseObject({\n schemaVersion: z.literal('1.0'),\n type: z.literal('written-response'),\n id: z.string().min(1),\n title: z.string().min(1),\n prompt: z.string(),\n promptHtml: z.string().optional(),\n minWords: z.number().int().min(0),\n maxWords: z.number().int().min(1),\n rubric: WrittenResponseRubricSchema.optional(),\n languageTarget: z.string().optional(),\n media: MediaSchema.optional(),\n feedback: FeedbackSchema.optional(),\n passThreshold: z.number().min(0).max(1).optional(),\n locale: z.string().optional(),\n learningObjectives: z.array(z.string()).optional(),\n difficultyLevel: z.literal([1, 2, 3, 4, 5]).optional(),\n })\n .refine((data) => data.maxWords >= data.minWords, {\n error: 'maxWords must be greater than or equal to minWords.',\n path: ['maxWords'],\n });\n","import { z } from 'zod/v4';\nimport { MediaSchema } from './media.js';\nimport { WrittenResponseRubricSchema } from './written-response.js';\n\n/**\n * Schemas for REDACTED activity data — the learner-safe projection `redact()`\n * produces (R7). Deliberately STRICT (`z.strictObject`), the opposite of the\n * loose content schemas: a redacted payload must prove the ABSENCE of every\n * answer-key and author-only field, so any unknown key is a validation\n * failure, not a passthrough. `assertRedacted` validates against these.\n */\n\n/** Shared fields every redacted activity carries. */\nconst redactedBase = {\n /** Marker distinguishing a redacted projection from full activity data. */\n redacted: z.literal(true),\n schemaVersion: z.literal('1.0'),\n id: z.string().min(1),\n title: z.string().min(1),\n media: MediaSchema.optional(),\n passThreshold: z.number().min(0).max(1).optional(),\n locale: z.string().optional(),\n learningObjectives: z.array(z.string()).optional(),\n difficultyLevel: z.literal([1, 2, 3, 4, 5]).optional(),\n};\n\n/** A redacted Multiple Choice option: id and display text only — no `isCorrect`, no feedback. */\nexport const RedactedMultipleChoiceOptionSchema = z.strictObject({\n id: z.string().min(1),\n text: z.string().min(1),\n});\n\n/**\n * Redacted Multiple Choice data: renderable (question, mode, options to pick\n * from) with the answer key, per-option feedback, overall feedback, and the\n * scoring strategy removed. `scoringStrategy` is answer-key by design: MC\n * `partial` carries a wrong-selection penalty `all-or-nothing` does not, so\n * knowing the strategy tells a learner whether guessing is free.\n */\nexport const RedactedMultipleChoiceDataSchema = z.strictObject({\n ...redactedBase,\n type: z.literal('multiple-choice'),\n question: z.string().min(1),\n questionHtml: z.string().optional(),\n mode: z.enum(['single', 'multi']),\n options: z.array(RedactedMultipleChoiceOptionSchema).min(2).max(26),\n shuffle: z.boolean().optional(),\n});\n\n/** A redacted blank: id and hint only — no accepted answers, no matching rules, no feedback. */\nexport const RedactedBlankConfigSchema = z.strictObject({\n id: z.string().min(1),\n hint: z.string().optional(),\n});\n\n/** Redacted Fill-in-the-Blanks data: passage and blank slots, key removed. */\nexport const RedactedFillInTheBlanksDataSchema = z.strictObject({\n ...redactedBase,\n type: z.literal('fill-in-the-blanks'),\n passage: z.string().min(1),\n passageHtml: z.string().optional(),\n blanks: z.array(RedactedBlankConfigSchema).min(1),\n});\n\n/**\n * Redacted Written Response data: the prompt, word bounds and rubric are\n * learner-visible (a rubric tells the learner what they are graded on);\n * authored pass/fail feedback is removed until the grade exists.\n */\nexport const RedactedWrittenResponseDataSchema = z.strictObject({\n ...redactedBase,\n type: z.literal('written-response'),\n prompt: z.string(),\n promptHtml: z.string().optional(),\n minWords: z.number().int().min(0),\n maxWords: z.number().int().min(1),\n rubric: WrittenResponseRubricSchema.optional(),\n languageTarget: z.string().optional(),\n});\n","/**\n * Configurable free-text answer matching (R3.3).\n *\n * Grade-stability contract: the DEFAULTS reproduce the v1 fill-in-the-blanks\n * semantics exactly (trim, then locale-insensitive lowercase, strict `===`).\n * Every tolerance below is opt-in, because changing a default here changes\n * historical grades — and this SDK powers real summative exams.\n */\nexport interface TextMatchPolicy {\n /** Compare case-sensitively. Default `false` (v1 behaviour). */\n caseSensitive?: boolean;\n /** Strip leading/trailing whitespace before comparing. Default `true` (v1 behaviour). */\n trim?: boolean;\n /**\n * Unicode normalization applied to both sides before comparing. Default\n * `'none'` (v1 behaviour). `'NFC'` makes a decomposed `está` (combining\n * U+0301) equal its composed form — the fix for accent-input mismatches in\n * EN/ES/PT content.\n */\n normalize?: 'none' | 'NFC' | 'NFKC';\n /** Treat diacritics as equal to their base letters (`está` ≡ `esta`). Default `false`. */\n foldDiacritics?: boolean;\n /** Collapse runs of inner whitespace to a single space. Default `false`. */\n collapseInnerWhitespace?: boolean;\n /** Ignore Unicode punctuation on both sides. Default `false`. */\n ignorePunctuation?: boolean;\n /** Maximum Levenshtein edit distance still accepted as a match. Default `0`. */\n levenshtein?: number;\n /**\n * BCP 47 tag for locale-aware case folding (e.g. `'tr'` for Turkish dotted /\n * dotless I). Default: locale-insensitive `toLowerCase()` (v1 behaviour).\n */\n locale?: string;\n}\n\n/**\n * How a match was achieved. Lets a consumer award full credit for an `exact`\n * match and partial credit for a `folded` or `fuzzy` one (e.g. diacritic\n * tolerance in a listening gap-fill vs. a spelling test).\n * - `exact` — equal under the baseline trim/case rules alone.\n * - `normalized` — required Unicode normalization, whitespace collapse, or punctuation tolerance.\n * - `folded` — required diacritic folding.\n * - `fuzzy` — required Levenshtein tolerance.\n * - `none` — no accepted answer matched.\n */\nexport interface TextMatchResult {\n matched: boolean;\n via: 'exact' | 'normalized' | 'folded' | 'fuzzy' | 'none';\n}\n\n/** Combining marks (any script) removed for diacritic folding after NFD decomposition. */\nconst COMBINING_MARKS_RE = /\\p{M}/gu;\n/**\n * Unicode punctuation (`\\p{P}`) removed by `ignorePunctuation`. Deliberately\n * NOT `\\p{S}`: symbols like `$`, `+`, `%` can be the substance of an answer\n * (`$100` vs `100`), so a \"punctuation\" toggle must not erase them.\n */\nconst PUNCTUATION_RE = /\\p{P}/gu;\n\nfunction applyBaseline(value: string, policy: TextMatchPolicy): string {\n let result = value;\n if (policy.trim !== false) {\n result = result.trim();\n }\n if (policy.caseSensitive !== true) {\n result = policy.locale ? result.toLocaleLowerCase(policy.locale) : result.toLowerCase();\n }\n return result;\n}\n\nfunction applyNormalization(value: string, policy: TextMatchPolicy): string {\n let result = value;\n if (policy.normalize === 'NFC' || policy.normalize === 'NFKC') {\n result = result.normalize(policy.normalize);\n }\n if (policy.ignorePunctuation === true) {\n result = result.replace(PUNCTUATION_RE, '');\n }\n if (policy.collapseInnerWhitespace === true) {\n // \"Inner\" means between non-space runs. Only trim the ends when the\n // policy's trim is on (its default) — an explicit `trim: false` must not\n // be silently overridden by the collapse.\n result = result.replace(/(?<=\\S)\\s+(?=\\S)/g, ' ');\n if (policy.trim !== false) {\n result = result.trim();\n }\n }\n return result;\n}\n\nfunction applyDiacriticFold(value: string): string {\n return value.normalize('NFD').replace(COMBINING_MARKS_RE, '').normalize('NFC');\n}\n\n/**\n * Levenshtein distance with an early-exit bound: returns `max + 1` as soon as\n * the distance provably exceeds `max`.\n */\nexport function levenshteinDistance(a: string, b: string, max: number): number {\n if (a === b) return 0;\n if (Math.abs(a.length - b.length) > max) return max + 1;\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n let previous = Array.from({ length: b.length + 1 }, (_, i) => i);\n for (let i = 1; i <= a.length; i += 1) {\n const current = [i];\n let rowMin = i;\n for (let j = 1; j <= b.length; j += 1) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n const value = Math.min(\n (previous[j] as number) + 1,\n (current[j - 1] as number) + 1,\n (previous[j - 1] as number) + cost,\n );\n current.push(value);\n if (value < rowMin) rowMin = value;\n }\n if (rowMin > max) return max + 1;\n previous = current;\n }\n return previous[b.length] as number;\n}\n\n/**\n * Matches a learner's input against one or more accepted answers under a\n * {@link TextMatchPolicy}. Tolerances are evaluated in stages — baseline\n * (trim/case), then normalization, then diacritic folding, then Levenshtein —\n * and {@link TextMatchResult.via} reports the first stage that produced the\n * match, so graders can award credit by match quality.\n *\n * With no policy (or an empty one) this is byte-for-byte the v1 matching\n * semantics: `trim` + locale-insensitive `toLowerCase` + strict equality.\n */\nexport function matchText(\n input: string,\n accepted: string | readonly string[],\n policy: TextMatchPolicy = {},\n): TextMatchResult {\n const acceptedList = typeof accepted === 'string' ? [accepted] : accepted;\n const baselineInput = applyBaseline(input, policy);\n const baselineAccepted = acceptedList.map((answer) => applyBaseline(answer, policy));\n\n if (baselineAccepted.some((answer) => answer === baselineInput)) {\n return { matched: true, via: 'exact' };\n }\n\n const usesNormalization =\n policy.normalize === 'NFC' ||\n policy.normalize === 'NFKC' ||\n policy.ignorePunctuation === true ||\n policy.collapseInnerWhitespace === true;\n const normalizedInput = usesNormalization\n ? applyNormalization(baselineInput, policy)\n : baselineInput;\n const normalizedAccepted = usesNormalization\n ? baselineAccepted.map((answer) => applyNormalization(answer, policy))\n : baselineAccepted;\n\n if (usesNormalization && normalizedAccepted.some((answer) => answer === normalizedInput)) {\n return { matched: true, via: 'normalized' };\n }\n\n const foldedInput =\n policy.foldDiacritics === true ? applyDiacriticFold(normalizedInput) : normalizedInput;\n const foldedAccepted =\n policy.foldDiacritics === true\n ? normalizedAccepted.map((answer) => applyDiacriticFold(answer))\n : normalizedAccepted;\n\n if (policy.foldDiacritics === true && foldedAccepted.some((answer) => answer === foldedInput)) {\n return { matched: true, via: 'folded' };\n }\n\n const maxDistance = policy.levenshtein ?? 0;\n if (\n maxDistance > 0 &&\n foldedAccepted.some(\n (answer) => levenshteinDistance(foldedInput, answer, maxDistance) <= maxDistance,\n )\n ) {\n return { matched: true, via: 'fuzzy' };\n }\n\n return { matched: false, via: 'none' };\n}\n","import type { z } from 'zod/v4';\nimport type { DeferredScoringPartial, ScoringResult } from '../types/activity.js';\n\n/** ScoringResult without `passed` — the public `score()` / `evaluate()` fill that in. */\nexport type PartialScoringResult = Omit<ScoringResult, 'passed'>;\n\n/**\n * Sensitivity classification of a single activity-data field, driving\n * `redact()`:\n * - `public` — safe to send to a learner before they answer.\n * - `answer-key` — reveals (or helps infer) the correct answer or the scoring\n * rules; removed unless `reveal: 'after-submit'` is requested.\n * - `author-only` — never leaves the authoring/grading context (e.g. rubrics).\n */\nexport type Sensitivity = 'public' | 'answer-key' | 'author-only';\n\n/**\n * Per-field sensitivity map for an activity type. A `Sensitivity` value\n * classifies the whole field (objects and arrays included); a nested\n * `FieldPolicy` recurses into an object field — and, for an array field,\n * applies to every element. Fail-closed: any field a policy does not mention\n * is treated as `author-only` and removed by `redact()` — adding a field\n * without classifying it hides it, never leaks it.\n */\nexport interface FieldPolicy {\n readonly [field: string]: Sensitivity | FieldPolicy;\n}\n\n/** xAPI interaction types defined by xAPI 1.0.3 (cmi.interaction vocabulary). */\nexport type XAPIInteractionType =\n | 'choice'\n | 'fill-in'\n | 'long-fill-in'\n | 'matching'\n | 'sequencing'\n | 'performance'\n | 'true-false'\n | 'other';\n\n/** Interop facts a generic statement builder cannot infer from data alone. */\nexport interface ActivityTypeInterop<TData> {\n /** IRI for the xAPI Activity `definition.type`. */\n readonly xapiActivityTypeIri?: string;\n /** xAPI `definition.interactionType` for this activity type. */\n readonly xapiInteractionType?: XAPIInteractionType;\n /** Builds the xAPI `correctResponsesPattern` strings for an item. */\n readonly correctResponsesPattern?: (data: TData) => string[];\n}\n\n/**\n * How responses to an activity type are graded:\n * - `sync` — a pure function produces the grade at submit time.\n * - `deferred` — grading happens asynchronously (AI or human) after\n * submission; `partial` reports the facts computable synchronously\n * (e.g. word counts), surfaced in `ItemOutcome.partial`.\n */\nexport type ActivityTypeScoring<TData, TResponse> =\n | {\n readonly kind: 'sync';\n readonly score: (data: TData, response: TResponse) => PartialScoringResult;\n }\n | {\n readonly kind: 'deferred';\n readonly reason: 'requires_async_grading';\n readonly partial?: (data: TData, response: TResponse | undefined) => DeferredScoringPartial;\n };\n\n/**\n * A value-level description of an activity type: its contract (schema), its\n * grading, its redaction policy, and its interop facts. Registering a\n * descriptor makes `validateActivity`, `score`, `evaluate`, `redact`, and\n * `jsonSchemaFor` work for the type — an activity type is a value, not a\n * hardcoded union member (R1).\n */\nexport interface ActivityTypeDescriptor<TData extends { type: string }, TResponse> {\n /** The `type` discriminator string (kebab-case by convention). */\n readonly type: TData['type'];\n /** Zod schema validating the activity's data contract. */\n readonly schema: z.ZodType<TData>;\n /** How responses are graded. */\n readonly scoring: ActivityTypeScoring<TData, TResponse>;\n /** Whether a response counts as an answer (vs. blank/untouched). */\n readonly isAnswered?: (response: TResponse | undefined) => boolean;\n /** Per-field sensitivity map driving `redact()`. Fail-closed. */\n readonly fieldPolicy?: FieldPolicy;\n /**\n * Schema the output of `redact(data)` (with default `reveal: 'none'`) must\n * satisfy. Strict by design: it proves the ABSENCE of answer-key fields,\n * so `assertRedacted` can guarantee a payload is safe to send to a learner.\n */\n readonly redactedSchema?: z.ZodType<unknown>;\n /** Interop facts for xAPI (and later QTI) statement building. */\n readonly interop?: ActivityTypeInterop<TData>;\n /** The interaction-event kinds components for this type emit. */\n readonly interactions?: readonly string[];\n}\n\n/**\n * Internal type-erased descriptor shape stored in the registry. Dispatch call\n * sites cast payloads back; the public generic API preserves inference.\n */\nexport interface RegisteredActivityTypeDescriptor {\n readonly type: string;\n readonly schema: z.ZodType<unknown>;\n readonly scoring:\n | {\n readonly kind: 'sync';\n readonly score: (data: unknown, response: unknown) => PartialScoringResult;\n }\n | {\n readonly kind: 'deferred';\n readonly reason: 'requires_async_grading';\n readonly partial?: (data: unknown, response: unknown) => DeferredScoringPartial;\n };\n readonly isAnswered?: (response: unknown) => boolean;\n readonly fieldPolicy?: FieldPolicy;\n readonly redactedSchema?: z.ZodType<unknown>;\n readonly interop?: ActivityTypeInterop<unknown>;\n readonly interactions?: readonly string[];\n}\n\n/**\n * Module-scoped default registry (deliberate — no registry instances until a\n * second consumer exists; see roadmap §3.2 / audit §15.3).\n */\nconst registry = new Map<string, RegisteredActivityTypeDescriptor>();\n\n/**\n * Identity helper that gives full type inference when authoring a descriptor:\n *\n * ```ts\n * const myType = defineActivityType<MyTypeData, MyTypeLearnerResponse>({ ... });\n * registerActivityType(myType);\n * ```\n */\nexport function defineActivityType<TData extends { type: string }, TResponse>(\n descriptor: ActivityTypeDescriptor<TData, TResponse>,\n): ActivityTypeDescriptor<TData, TResponse> {\n return descriptor;\n}\n\n/**\n * Registers an activity type on the default registry, making it live for\n * `validateActivity`, `score`, `evaluate`, `redact`, and `jsonSchemaFor`.\n * Re-registering the same descriptor object is a no-op; registering a\n * DIFFERENT descriptor under an existing type throws — silently replacing a\n * type's contract or scoring is exactly the class of accident a summative\n * SDK must not allow.\n */\nexport function registerActivityType<TData extends { type: string }, TResponse>(\n descriptor: ActivityTypeDescriptor<TData, TResponse>,\n): void {\n if (descriptor.type === 'item-group') {\n // The container that holds several items around one stimulus. It has no\n // learner response and no score of its own, so it can never satisfy this\n // contract — and letting a consumer register one would make `isItemGroup`\n // and `flattenSequence` misread their own container.\n throw new Error(\n '\"item-group\" is reserved for the SDK\\'s item-group container (see ItemGroup) and cannot be registered as an activity type.',\n );\n }\n const existing = registry.get(descriptor.type);\n if (existing !== undefined) {\n if ((existing as unknown) === (descriptor as unknown)) {\n return;\n }\n throw new Error(\n `Activity type \"${descriptor.type}\" is already registered. ` +\n 'Registering a different descriptor for an existing type is not allowed.',\n );\n }\n registry.set(descriptor.type, descriptor as unknown as RegisteredActivityTypeDescriptor);\n}\n\n/** Returns the registered descriptor for `type`, or `undefined`. */\nexport function getActivityTypeDescriptor(\n type: string,\n): RegisteredActivityTypeDescriptor | undefined {\n return registry.get(type);\n}\n\n/** The type strings currently registered (built-ins plus consumer-registered). */\nexport function registeredActivityTypes(): string[] {\n return [...registry.keys()];\n}\n","/**\n * All-or-nothing scoring: full credit only when every item is correct.\n *\n * @param correctItems - per-item correctness flags for the relevant items\n * @returns `1` if every item is correct (or the list is empty), otherwise `0`\n */\nexport function allOrNothingStrategy(correctItems: boolean[]): number {\n return correctItems.every((isCorrect) => isCorrect) ? 1 : 0;\n}\n","/**\n * Partial-credit scoring for Multiple Choice — balanced/symmetric scheme.\n *\n * Reward and penalty are each normalised by their own pool: the fraction of\n * correct options found, minus the fraction of distractors wrongly chosen.\n * This is fairer than normalising the penalty by the correct-option count —\n * under that older form a single wrong pick could zero an otherwise-correct\n * response when there was only one correct answer.\n *\n * Result is always in [0, 1]: `reward ∈ [0,1]` and `penalty ∈ [0,1]`, so\n * `reward - penalty ∈ [-1,1]` and `max(0, …)` floors it at 0. Selecting every\n * option yields `1 - 1 = 0`; selecting exactly the correct set yields `1`.\n *\n * @param correctSelected - number of selected options that are correct\n * @param incorrectSelected - number of selected options that are incorrect\n * @param totalCorrect - total correct options; the `MultipleChoiceDataSchema`\n * \"≥1 correct\" guard guarantees this is ≥ 1, so the reward term cannot\n * divide by zero for schema-validated data\n * @param totalIncorrect - total incorrect options (distractors); when `0`\n * (every option is correct) the penalty term is defined as `0`\n */\nexport function partialStrategy(\n correctSelected: number,\n incorrectSelected: number,\n totalCorrect: number,\n totalIncorrect: number,\n): number {\n const reward = correctSelected / totalCorrect;\n const penalty = totalIncorrect === 0 ? 0 : incorrectSelected / totalIncorrect;\n return Math.max(0, reward - penalty);\n}\n\n/**\n * Partial-credit scoring for Fill-in-the-Blanks: fraction of blanks answered\n * correctly. Each blank is independently right or wrong, so a plain proportion\n * is already fair — no penalty term applies.\n *\n * @param correctBlanks - number of blanks answered correctly\n * @param totalBlanks - total number of blanks; the `FillInTheBlanksDataSchema`\n * `blanks.min(1)` constraint guarantees this is ≥ 1, so division by zero\n * cannot occur for schema-validated data\n */\nexport function partialBlankStrategy(correctBlanks: number, totalBlanks: number): number {\n return correctBlanks / totalBlanks;\n}\n","import type {\n BlankConfig,\n FillInTheBlanksData,\n FillInTheBlanksLearnerResponse,\n ScoringDetail,\n} from '../../types/activity.js';\nimport { allOrNothingStrategy } from '../strategies/all-or-nothing.js';\nimport { partialBlankStrategy } from '../strategies/partial.js';\nimport { matchText, type TextMatchPolicy } from '../text-match.js';\nimport type { PartialScoringResult } from './multiple-choice.js';\n\n/**\n * Resolves the effective match policy for a blank. The legacy\n * `caseSensitive` / `trimWhitespace` flags map onto the baseline policy\n * fields; an explicit `blank.match` policy takes precedence field-by-field.\n * With neither present, the result is the v1 semantics exactly.\n */\nfunction policyFor(blank: BlankConfig): TextMatchPolicy {\n return {\n ...(blank.caseSensitive !== undefined ? { caseSensitive: blank.caseSensitive } : {}),\n ...(blank.trimWhitespace !== undefined ? { trim: blank.trimWhitespace } : {}),\n ...blank.match,\n };\n}\n\n/**\n * Scores a Fill-in-the-Blanks response. Each blank is evaluated independently\n * via {@link matchText} under the blank's resolved policy; a missing answer\n * key is treated as empty input and scored incorrect.\n */\nexport function scoreFillInTheBlanks(\n data: FillInTheBlanksData,\n response: FillInTheBlanksLearnerResponse,\n): PartialScoringResult {\n const details: ScoringDetail[] = [];\n const perBlankCorrect: boolean[] = [];\n\n for (const blank of data.blanks) {\n const rawInput = response.answers[blank.id];\n const input = typeof rawInput === 'string' ? rawInput : '';\n const matched = matchText(input, blank.acceptedAnswers, policyFor(blank)).matched;\n\n perBlankCorrect.push(matched);\n details.push({\n itemId: blank.id,\n correct: matched,\n outcome: matched ? 'correct' : 'incorrect',\n learnerResponse: [input],\n correctResponse: [...blank.acceptedAnswers],\n weight: 1,\n });\n }\n\n const correctBlanks = perBlankCorrect.filter(Boolean).length;\n const scoreValue =\n data.scoringStrategy === 'all-or-nothing'\n ? allOrNothingStrategy(perBlankCorrect)\n : partialBlankStrategy(correctBlanks, data.blanks.length);\n\n return { score: scoreValue, maxScore: 1, feedback: null, details };\n}\n","import type { PartialScoringResult } from '../../registry/registry.js';\nimport type {\n MultipleChoiceData,\n MultipleChoiceLearnerResponse,\n ScoringDetail,\n} from '../../types/activity.js';\nimport { partialStrategy } from '../strategies/partial.js';\n\nexport type { PartialScoringResult } from '../../registry/registry.js';\n\n/**\n * Scores a Multiple Choice response. Pure; trusts its typed inputs (validation\n * is the component boundary's job). Options are looked up by id; an unknown\n * selected id is treated as an incorrect selection.\n */\nexport function scoreMultipleChoice(\n data: MultipleChoiceData,\n response: MultipleChoiceLearnerResponse,\n): PartialScoringResult {\n const optionById = new Map(data.options.map((option) => [option.id, option]));\n const selected = new Set(response.selectedOptionIds);\n\n const totalCorrect = data.options.filter((option) => option.isCorrect).length;\n const totalIncorrect = data.options.length - totalCorrect;\n\n let scoreValue: number;\n\n if (data.scoringStrategy === 'all-or-nothing') {\n if (data.mode === 'single') {\n scoreValue =\n response.selectedOptionIds.length === 1 &&\n optionById.get(response.selectedOptionIds[0])?.isCorrect === true\n ? 1\n : 0;\n } else {\n const correctIds = data.options.filter((o) => o.isCorrect).map((o) => o.id);\n const allCorrectSelected = correctIds.every((id) => selected.has(id));\n scoreValue = selected.size === correctIds.length && allCorrectSelected ? 1 : 0;\n }\n } else {\n let correctSelected = 0;\n let incorrectSelected = 0;\n for (const id of selected) {\n const option = optionById.get(id);\n if (option?.isCorrect) {\n correctSelected += 1;\n } else {\n incorrectSelected += 1;\n }\n }\n scoreValue = partialStrategy(correctSelected, incorrectSelected, totalCorrect, totalIncorrect);\n }\n\n const details: ScoringDetail[] = data.options.map((option) => {\n const wasSelected = selected.has(option.id);\n const outcome = wasSelected\n ? option.isCorrect\n ? 'correct'\n : 'incorrect'\n : option.isCorrect\n ? 'incorrect-omission'\n : 'correct-omission';\n return {\n itemId: option.id,\n correct: wasSelected === option.isCorrect,\n outcome,\n learnerResponse: [wasSelected ? 'selected' : 'not-selected'],\n correctResponse: [option.isCorrect ? 'selected' : 'not-selected'],\n weight: 1,\n };\n });\n\n return { score: scoreValue, maxScore: 1, feedback: null, details };\n}\n","import type { z } from 'zod/v4';\nimport { countWords } from '../count-words.js';\nimport { FillInTheBlanksDataSchema } from '../schemas/fill-in-the-blanks.js';\nimport { MultipleChoiceDataSchema } from '../schemas/multiple-choice.js';\nimport {\n RedactedFillInTheBlanksDataSchema,\n RedactedMultipleChoiceDataSchema,\n RedactedWrittenResponseDataSchema,\n} from '../schemas/redacted.js';\nimport { WrittenResponseDataSchema } from '../schemas/written-response.js';\nimport { scoreFillInTheBlanks } from '../scoring/activity-scorers/fill-in-the-blanks.js';\nimport { scoreMultipleChoice } from '../scoring/activity-scorers/multiple-choice.js';\nimport type {\n FillInTheBlanksData,\n FillInTheBlanksLearnerResponse,\n MultipleChoiceData,\n MultipleChoiceLearnerResponse,\n WrittenResponseData,\n WrittenResponseLearnerResponse,\n} from '../types/activity.js';\nimport { defineActivityType, type FieldPolicy, registerActivityType } from './registry.js';\n\n/**\n * Field-sensitivity policies for the built-in types (R7). Fail-closed:\n * anything not listed here is dropped by `redact()`. `scoringStrategy` is\n * answer-key everywhere — MC `partial` penalises wrong selections while\n * `all-or-nothing` does not, so the strategy reveals whether guessing is\n * free. Authored feedback is answer-key (it may quote or hint the answer);\n * rubrics are author-only (grader assets).\n */\nconst SHARED_PUBLIC_FIELDS: FieldPolicy = {\n schemaVersion: 'public',\n type: 'public',\n id: 'public',\n title: 'public',\n media: 'public',\n passThreshold: 'public',\n locale: 'public',\n learningObjectives: 'public',\n difficultyLevel: 'public',\n};\n\nconst MULTIPLE_CHOICE_FIELD_POLICY: FieldPolicy = {\n ...SHARED_PUBLIC_FIELDS,\n question: 'public',\n questionHtml: 'public',\n mode: 'public',\n shuffle: 'public',\n scoringStrategy: 'answer-key',\n feedback: 'answer-key',\n options: {\n id: 'public',\n text: 'public',\n isCorrect: 'answer-key',\n feedback: 'answer-key',\n },\n};\n\nconst FILL_IN_THE_BLANKS_FIELD_POLICY: FieldPolicy = {\n ...SHARED_PUBLIC_FIELDS,\n passage: 'public',\n passageHtml: 'public',\n scoringStrategy: 'answer-key',\n feedback: 'answer-key',\n blanks: {\n id: 'public',\n hint: 'public',\n acceptedAnswers: 'answer-key',\n caseSensitive: 'answer-key',\n trimWhitespace: 'answer-key',\n match: 'answer-key',\n feedback: 'answer-key',\n },\n};\n\nconst WRITTEN_RESPONSE_FIELD_POLICY: FieldPolicy = {\n ...SHARED_PUBLIC_FIELDS,\n prompt: 'public',\n promptHtml: 'public',\n minWords: 'public',\n maxWords: 'public',\n languageTarget: 'public',\n feedback: 'answer-key',\n // A rubric is a LEARNER affordance, not a grader secret: it tells the\n // learner what they are being graded on, which is pedagogically the point\n // of publishing one. (Classifying it author-only broke real deployments\n // that render a rubric panel during the attempt.) A deployment that wants\n // it hidden can tighten this per call via `redact(data, { policy })`.\n rubric: 'public',\n};\n\n/** Built-in Multiple Choice descriptor. */\nexport const multipleChoiceType = defineActivityType<\n MultipleChoiceData,\n MultipleChoiceLearnerResponse\n>({\n type: 'multiple-choice',\n // zod4 optional outputs are `T | undefined`; the hand-written wire types use\n // exact optionals. Structurally identical at runtime — cast is type-level only.\n schema: MultipleChoiceDataSchema as unknown as z.ZodType<MultipleChoiceData>,\n scoring: { kind: 'sync', score: scoreMultipleChoice },\n isAnswered: (response) => (response?.selectedOptionIds.length ?? 0) > 0,\n fieldPolicy: MULTIPLE_CHOICE_FIELD_POLICY,\n redactedSchema: RedactedMultipleChoiceDataSchema,\n interop: {\n xapiActivityTypeIri: 'http://adlnet.gov/expapi/activities/cmi.interaction',\n xapiInteractionType: 'choice',\n correctResponsesPattern: (data) => [\n data.options\n .filter((option) => option.isCorrect)\n .map((option) => option.id)\n .join('[,]'),\n ],\n },\n interactions: ['option-selected', 'option-deselected', 'submitted'],\n});\n\n/** Built-in Fill-in-the-Blanks descriptor. */\nexport const fillInTheBlanksType = defineActivityType<\n FillInTheBlanksData,\n FillInTheBlanksLearnerResponse\n>({\n type: 'fill-in-the-blanks',\n schema: FillInTheBlanksDataSchema as unknown as z.ZodType<FillInTheBlanksData>,\n scoring: { kind: 'sync', score: scoreFillInTheBlanks },\n isAnswered: (response) =>\n Object.values(response?.answers ?? {}).some((answer) => answer.trim().length > 0),\n fieldPolicy: FILL_IN_THE_BLANKS_FIELD_POLICY,\n redactedSchema: RedactedFillInTheBlanksDataSchema,\n interop: {\n xapiActivityTypeIri: 'http://adlnet.gov/expapi/activities/cmi.interaction',\n xapiInteractionType: 'fill-in',\n // xAPI fill-in pattern: blank answers joined with \"[,]\". Only the first\n // accepted answer per blank is emitted (full alternates would explode\n // combinatorially); the complete key lives in the activity data.\n correctResponsesPattern: (data) => [\n data.blanks.map((blank) => blank.acceptedAnswers[0] ?? '').join('[,]'),\n ],\n },\n interactions: ['blank-filled', 'hint-requested', 'submitted'],\n});\n\n/**\n * Built-in Written Response descriptor. Grading is DEFERRED: submissions are\n * graded asynchronously (AI or human) by the consumer; the synchronous\n * outcome reports only word-count facts. `wordCount` is recomputed from the\n * submitted text with the canonical `countWords()` — the client-supplied\n * count is informational, never trusted.\n */\nexport const writtenResponseType = defineActivityType<\n WrittenResponseData,\n WrittenResponseLearnerResponse\n>({\n type: 'written-response',\n schema: WrittenResponseDataSchema as unknown as z.ZodType<WrittenResponseData>,\n scoring: {\n kind: 'deferred',\n reason: 'requires_async_grading',\n partial: (data, response) => {\n const wordCount = countWords(response?.text ?? '');\n return {\n withinWordBounds:\n response !== undefined && wordCount >= data.minWords && wordCount <= data.maxWords,\n wordCount,\n };\n },\n },\n isAnswered: (response) => (response?.text.trim().length ?? 0) > 0,\n fieldPolicy: WRITTEN_RESPONSE_FIELD_POLICY,\n redactedSchema: RedactedWrittenResponseDataSchema,\n interop: {\n xapiActivityTypeIri: 'http://adlnet.gov/expapi/activities/cmi.interaction',\n xapiInteractionType: 'long-fill-in',\n correctResponsesPattern: () => [],\n },\n interactions: ['text-changed', 'submitted'],\n});\n\nregisterActivityType(multipleChoiceType);\nregisterActivityType(fillInTheBlanksType);\nregisterActivityType(writtenResponseType);\n"]}
|