@intellectif/lk-core 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/dist/activity-D5tqgx8A.d.cts +330 -0
  3. package/dist/activity-D5tqgx8A.d.ts +330 -0
  4. package/dist/chunk-2T3IL7VL.js +147 -0
  5. package/dist/chunk-2T3IL7VL.js.map +1 -0
  6. package/dist/chunk-3JMLUUFS.cjs +124 -0
  7. package/dist/chunk-3JMLUUFS.cjs.map +1 -0
  8. package/dist/chunk-6VOCV4EX.js +25 -0
  9. package/dist/chunk-6VOCV4EX.js.map +1 -0
  10. package/dist/chunk-DCDOTQNH.cjs +147 -0
  11. package/dist/chunk-DCDOTQNH.cjs.map +1 -0
  12. package/dist/chunk-DV466SBJ.cjs +25 -0
  13. package/dist/chunk-DV466SBJ.cjs.map +1 -0
  14. package/dist/chunk-GZ73G2TS.js +124 -0
  15. package/dist/chunk-GZ73G2TS.js.map +1 -0
  16. package/dist/chunk-PJFDAP54.cjs +188 -0
  17. package/dist/chunk-PJFDAP54.cjs.map +1 -0
  18. package/dist/chunk-ZHOX6TO4.js +188 -0
  19. package/dist/chunk-ZHOX6TO4.js.map +1 -0
  20. package/dist/index.cjs +45 -0
  21. package/dist/index.cjs.map +1 -0
  22. package/dist/index.d.cts +56 -0
  23. package/dist/index.d.ts +56 -0
  24. package/dist/index.js +45 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/schemas.cjs +23 -0
  27. package/dist/schemas.cjs.map +1 -0
  28. package/dist/schemas.d.cts +179 -0
  29. package/dist/schemas.d.ts +179 -0
  30. package/dist/schemas.js +23 -0
  31. package/dist/schemas.js.map +1 -0
  32. package/dist/scoring.cjs +10 -0
  33. package/dist/scoring.cjs.map +1 -0
  34. package/dist/scoring.d.cts +20 -0
  35. package/dist/scoring.d.ts +20 -0
  36. package/dist/scoring.js +10 -0
  37. package/dist/scoring.js.map +1 -0
  38. package/dist/xapi.cjs +14 -0
  39. package/dist/xapi.cjs.map +1 -0
  40. package/dist/xapi.d.cts +88 -0
  41. package/dist/xapi.d.ts +88 -0
  42. package/dist/xapi.js +14 -0
  43. package/dist/xapi.js.map +1 -0
  44. package/package.json +50 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Intellectif LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,330 @@
1
+ /**
2
+ * An xAPI Activity object — the thing a statement is about.
3
+ * This SDK only builds Activity-type objects (xAPI 1.0.3 §4.1.4.1).
4
+ */
5
+ interface XAPIObject {
6
+ objectType: 'Activity';
7
+ /** IRI uniquely identifying the activity. */
8
+ id: string;
9
+ definition?: {
10
+ /** Language-map of human-readable names for the activity. */
11
+ name?: Record<string, string>;
12
+ /** Language-map of human-readable descriptions. */
13
+ description?: Record<string, string>;
14
+ /** IRI identifying the activity type. */
15
+ type?: string;
16
+ extensions?: Record<string, unknown>;
17
+ };
18
+ }
19
+ /**
20
+ * An xAPI Agent (person or system) that performed the statement's verb.
21
+ * Exactly one of `mbox` or `account` must be present.
22
+ */
23
+ interface XAPIActor {
24
+ objectType: 'Agent';
25
+ /** Display name for the actor. */
26
+ name?: string;
27
+ /** Mailto IRI uniquely identifying the actor (e.g. `mailto:user@example.com`). */
28
+ mbox?: string;
29
+ /** Account-based actor identifier. */
30
+ account?: {
31
+ /** IRI of the home page of the account's service provider. */
32
+ homePage: string;
33
+ /** The actor's unique name on that service. */
34
+ name: string;
35
+ };
36
+ }
37
+ /** An xAPI Verb — the action performed in the statement. */
38
+ interface XAPIVerbObject {
39
+ /** IRI uniquely identifying this verb (e.g. from adlnet.gov/expapi/verbs). */
40
+ id: string;
41
+ /** Language-map of human-readable verb labels. */
42
+ display: Record<string, string>;
43
+ }
44
+ /** Score sub-object within an xAPI Result. */
45
+ interface XAPIScore {
46
+ /** Normalized score in the range [0, 1]. */
47
+ scaled: number;
48
+ /** Raw score in the instrument's native scale. */
49
+ raw?: number;
50
+ /** Minimum possible raw score. */
51
+ min?: number;
52
+ /** Maximum possible raw score. */
53
+ max?: number;
54
+ }
55
+ /** Result information attached to an xAPI Statement. */
56
+ interface XAPIResult {
57
+ score?: XAPIScore;
58
+ /** Whether the actor's performance was successful. */
59
+ success?: boolean;
60
+ /** Whether the activity was completed. */
61
+ completion?: boolean;
62
+ /** ISO 8601 duration string representing time spent (e.g. `PT5M30S`). */
63
+ duration?: string;
64
+ /** A string representation of the learner's response. */
65
+ response?: string;
66
+ extensions?: Record<string, unknown>;
67
+ }
68
+ /** Context information attached to an xAPI Statement. */
69
+ interface XAPIContext {
70
+ /** Name of the software used to record the statement. */
71
+ platform?: string;
72
+ /** BCP 47 language tag for the activity content. */
73
+ language?: string;
74
+ extensions?: Record<string, unknown>;
75
+ }
76
+ /** A complete xAPI 1.0.3 Statement. */
77
+ interface XAPIStatement {
78
+ /** UUID v4 uniquely identifying this statement. */
79
+ id: string;
80
+ actor: XAPIActor;
81
+ verb: XAPIVerbObject;
82
+ object: XAPIObject;
83
+ result?: XAPIResult;
84
+ context?: XAPIContext;
85
+ /** ISO 8601 timestamp of when the statement occurred. */
86
+ timestamp: string;
87
+ version: '1.0.3';
88
+ }
89
+ /**
90
+ * Configuration passed to `useXAPI` to connect to an LRS endpoint.
91
+ * The `auth` field supports both HTTP Basic and Bearer token authentication.
92
+ */
93
+ interface XAPIConfig {
94
+ /** Full URL of the LRS statements endpoint. */
95
+ endpoint: string;
96
+ /** Authentication credentials for the LRS. */
97
+ auth: {
98
+ type: 'basic';
99
+ username: string;
100
+ password: string;
101
+ } | {
102
+ type: 'bearer';
103
+ token: string;
104
+ };
105
+ /** IRI identifying the activity in all statements sent by this hook instance. */
106
+ activityId: string;
107
+ actor: XAPIActor;
108
+ /** Called after all retry attempts are exhausted. */
109
+ onError?: (err: XAPIError) => void;
110
+ }
111
+ /** Error payload delivered to `XAPIConfig.onError` after retries are exhausted. */
112
+ interface XAPIError {
113
+ /** The statement that failed to send. */
114
+ statement: XAPIStatement;
115
+ /** The 1-based attempt number on which the final failure occurred. */
116
+ attempt: number;
117
+ /** HTTP status code from the LRS, if a response was received. */
118
+ statusCode?: number;
119
+ /** Human-readable error description. */
120
+ message: string;
121
+ }
122
+
123
+ /** The set of activity types supported by learning-kit. */
124
+ type ActivityType = 'multiple-choice' | 'fill-in-the-blanks';
125
+ /** Maps each ActivityType string to its corresponding data shape. */
126
+ interface ActivityDataMap {
127
+ 'multiple-choice': MultipleChoiceData;
128
+ 'fill-in-the-blanks': FillInTheBlanksData;
129
+ }
130
+ /** Union of all valid activity data shapes. */
131
+ type ActivityData = MultipleChoiceData | FillInTheBlanksData;
132
+ /**
133
+ * Optional media (image/audio/video) shown above a question or passage.
134
+ * URL-only: hosting/delivery is the consuming application's responsibility.
135
+ */
136
+ interface ActivityMedia {
137
+ /** The kind of media; selects the rendered element. `embed` → sandboxed iframe. */
138
+ type: 'image' | 'audio' | 'video' | 'embed';
139
+ /**
140
+ * Source URL. For `embed` this MUST be the provider's embeddable URL
141
+ * (e.g. `https://www.youtube.com/embed/<id>`), not the watch page.
142
+ */
143
+ url: string;
144
+ /** Alternative text. Required for `image` and `embed`; optional label for audio/video. */
145
+ alt?: string;
146
+ /** Optional WebVTT captions track URL for `audio`/`video`. */
147
+ captionsUrl?: string;
148
+ }
149
+ /**
150
+ * Optional authored "overall feedback" shown after submission, chosen by
151
+ * whether the learner passed (h5p-style overall feedback). Distinct from
152
+ * per-option feedback; either field may be omitted.
153
+ */
154
+ interface ActivityFeedback {
155
+ /** Shown when the learner passes (score ≥ pass threshold). */
156
+ correct?: string;
157
+ /** Shown when the learner does not pass. */
158
+ incorrect?: string;
159
+ }
160
+ /** A single selectable option within a Multiple Choice activity. */
161
+ interface MultipleChoiceOption {
162
+ /** Unique identifier for this option within the activity. */
163
+ id: string;
164
+ /** Display text shown to the learner. */
165
+ text: string;
166
+ /** Whether this option is part of the correct answer. */
167
+ isCorrect: boolean;
168
+ /** Optional per-option feedback shown after submission. */
169
+ feedback?: string;
170
+ }
171
+ /** Data contract for a Multiple Choice activity. */
172
+ interface MultipleChoiceData {
173
+ schemaVersion: '1.0';
174
+ type: 'multiple-choice';
175
+ /** Unique identifier for this activity. */
176
+ id: string;
177
+ /** Human-readable title used in xAPI statements and error boundaries. */
178
+ title: string;
179
+ /** The question stem presented to the learner. */
180
+ question: string;
181
+ /** `single` allows one selection; `multi` allows multiple. */
182
+ mode: 'single' | 'multi';
183
+ /** Ordered list of answer options. */
184
+ options: MultipleChoiceOption[];
185
+ /** Scoring algorithm applied when the learner submits. */
186
+ scoringStrategy: 'all-or-nothing' | 'partial';
187
+ /** Optional media shown above the question. */
188
+ media?: ActivityMedia;
189
+ /** Optional authored overall feedback shown after submission. */
190
+ feedback?: ActivityFeedback;
191
+ /** Minimum scaled score [0–1] required to pass. Defaults to 0.6 when absent. */
192
+ passThreshold?: number;
193
+ /** When true, options are shuffled deterministically per session. */
194
+ shuffle?: boolean;
195
+ /** BCP 47 language tag for the activity content. */
196
+ locale?: string;
197
+ /** IRI references to learning objectives addressed by this activity. */
198
+ learningObjectives?: string[];
199
+ /** Subjective difficulty on a 1–5 scale. */
200
+ difficultyLevel?: 1 | 2 | 3 | 4 | 5;
201
+ }
202
+ /** Configuration for a single fill-in-the-blank slot. */
203
+ interface BlankConfig {
204
+ /** Unique identifier matching the `{{blank_id}}` placeholder in the passage. */
205
+ id: string;
206
+ /** List of strings accepted as correct answers for this blank. */
207
+ acceptedAnswers: string[];
208
+ /** Whether answer matching is case-sensitive. Defaults to false. */
209
+ caseSensitive?: boolean;
210
+ /** Whether leading/trailing whitespace is stripped before matching. Defaults to true. */
211
+ trimWhitespace?: boolean;
212
+ /** Optional hint text revealed on learner request. */
213
+ hint?: string;
214
+ /** Optional feedback shown inline next to this blank after submission. */
215
+ feedback?: string;
216
+ }
217
+ /** Data contract for a Fill-in-the-Blanks activity. */
218
+ interface FillInTheBlanksData {
219
+ schemaVersion: '1.0';
220
+ type: 'fill-in-the-blanks';
221
+ /** Unique identifier for this activity. */
222
+ id: string;
223
+ /** Human-readable title used in xAPI statements and error boundaries. */
224
+ title: string;
225
+ /** Passage text containing `{{blank_id}}` placeholders. */
226
+ passage: string;
227
+ /** Configuration for each blank in the passage. */
228
+ blanks: BlankConfig[];
229
+ /** Scoring algorithm applied when the learner submits. */
230
+ scoringStrategy: 'all-or-nothing' | 'partial';
231
+ /** Optional media shown above the passage. */
232
+ media?: ActivityMedia;
233
+ /** Optional authored overall feedback shown after submission. */
234
+ feedback?: ActivityFeedback;
235
+ /** Minimum scaled score [0–1] required to pass. Defaults to 0.6 when absent. */
236
+ passThreshold?: number;
237
+ /** BCP 47 language tag for the activity content. */
238
+ locale?: string;
239
+ /** IRI references to learning objectives addressed by this activity. */
240
+ learningObjectives?: string[];
241
+ /** Subjective difficulty on a 1–5 scale. */
242
+ difficultyLevel?: 1 | 2 | 3 | 4 | 5;
243
+ }
244
+ /** Union of all learner response shapes. */
245
+ type LearnerResponse = MultipleChoiceLearnerResponse | FillInTheBlanksLearnerResponse;
246
+ /** Learner response for a Multiple Choice activity. */
247
+ interface MultipleChoiceLearnerResponse {
248
+ type: 'multiple-choice';
249
+ /** IDs of the options the learner selected. */
250
+ selectedOptionIds: string[];
251
+ }
252
+ /** Learner response for a Fill-in-the-Blanks activity. */
253
+ interface FillInTheBlanksLearnerResponse {
254
+ type: 'fill-in-the-blanks';
255
+ /** Map of blank ID to the learner's typed answer. */
256
+ answers: Record<string, string>;
257
+ }
258
+ /** Per-item scoring breakdown returned by the scoring engine. */
259
+ interface ScoringDetail {
260
+ /** ID of the option or blank this detail refers to. */
261
+ itemId: string;
262
+ /** Whether the learner's response for this item was correct. */
263
+ correct: boolean;
264
+ /** The learner's actual response for this item. */
265
+ learnerResponse: string | string[];
266
+ /** The expected correct response(s) for this item. */
267
+ correctResponse: string | string[];
268
+ /** Optional weight applied to this item's contribution to the overall score. */
269
+ weight?: number;
270
+ }
271
+ /** Full scoring result returned by the scoring engine. */
272
+ interface ScoringResult {
273
+ /** Scaled score in the range [0, 1]. */
274
+ score: number;
275
+ /** Maximum possible scaled score (always 1). */
276
+ maxScore: number;
277
+ /** Whether the score meets or exceeds the activity's pass threshold. */
278
+ passed: boolean;
279
+ /** Optional top-level feedback message, or null when absent. */
280
+ feedback: string | null;
281
+ /** Per-item scoring breakdown. */
282
+ details: ScoringDetail[];
283
+ }
284
+ /** A single validation error produced by `validateActivity`. */
285
+ interface ValidationError {
286
+ /** JSON-path-style location of the invalid field. */
287
+ path: string[];
288
+ /** Human-readable description of the validation failure. */
289
+ message: string;
290
+ /** Machine-readable error code. */
291
+ code: string;
292
+ }
293
+ /**
294
+ * Result of a `validateActivity` call.
295
+ * On success, `data` is the validated and typed activity data.
296
+ * On failure, `errors` contains one entry per violated constraint.
297
+ */
298
+ type ValidationResult<T> = {
299
+ success: true;
300
+ data: T;
301
+ } | {
302
+ success: false;
303
+ errors: ValidationError[];
304
+ };
305
+ /** The payload delivered to an activity's `onComplete` callback. */
306
+ interface ActivityResult {
307
+ /** Scaled score in the range [0, 1]. */
308
+ score: number;
309
+ /** Maximum possible scaled score (always 1). */
310
+ maxScore: number;
311
+ /** Whether the learner passed based on the activity's pass threshold. */
312
+ passed: boolean;
313
+ /** Time in milliseconds from first interaction to submission. */
314
+ timeSpent: number;
315
+ /** The xAPI statement built and (optionally) sent for this attempt. */
316
+ xapiStatement: XAPIStatement;
317
+ }
318
+ /** Fired by activity components on every discrete learner interaction. */
319
+ interface InteractionEvent {
320
+ /** The kind of interaction that occurred. */
321
+ type: 'option-selected' | 'option-deselected' | 'blank-filled' | 'hint-requested' | 'submitted';
322
+ /** ID of the activity that produced the event. */
323
+ activityId: string;
324
+ /** Unix timestamp (ms) of when the interaction occurred. */
325
+ timestamp: number;
326
+ /** Additional event-specific data. */
327
+ payload: Record<string, unknown>;
328
+ }
329
+
330
+ export type { ActivityData as A, BlankConfig as B, FillInTheBlanksData as F, InteractionEvent as I, LearnerResponse as L, MultipleChoiceData as M, ScoringDetail as S, ValidationError as V, XAPIActor as X, ActivityDataMap as a, ActivityFeedback as b, ActivityMedia as c, ActivityResult as d, ActivityType as e, FillInTheBlanksLearnerResponse as f, MultipleChoiceLearnerResponse as g, MultipleChoiceOption as h, ScoringResult as i, ValidationResult as j, XAPIConfig as k, XAPIContext as l, XAPIError as m, XAPIObject as n, XAPIResult as o, XAPIScore as p, XAPIStatement as q, XAPIVerbObject as r };
@@ -0,0 +1,330 @@
1
+ /**
2
+ * An xAPI Activity object — the thing a statement is about.
3
+ * This SDK only builds Activity-type objects (xAPI 1.0.3 §4.1.4.1).
4
+ */
5
+ interface XAPIObject {
6
+ objectType: 'Activity';
7
+ /** IRI uniquely identifying the activity. */
8
+ id: string;
9
+ definition?: {
10
+ /** Language-map of human-readable names for the activity. */
11
+ name?: Record<string, string>;
12
+ /** Language-map of human-readable descriptions. */
13
+ description?: Record<string, string>;
14
+ /** IRI identifying the activity type. */
15
+ type?: string;
16
+ extensions?: Record<string, unknown>;
17
+ };
18
+ }
19
+ /**
20
+ * An xAPI Agent (person or system) that performed the statement's verb.
21
+ * Exactly one of `mbox` or `account` must be present.
22
+ */
23
+ interface XAPIActor {
24
+ objectType: 'Agent';
25
+ /** Display name for the actor. */
26
+ name?: string;
27
+ /** Mailto IRI uniquely identifying the actor (e.g. `mailto:user@example.com`). */
28
+ mbox?: string;
29
+ /** Account-based actor identifier. */
30
+ account?: {
31
+ /** IRI of the home page of the account's service provider. */
32
+ homePage: string;
33
+ /** The actor's unique name on that service. */
34
+ name: string;
35
+ };
36
+ }
37
+ /** An xAPI Verb — the action performed in the statement. */
38
+ interface XAPIVerbObject {
39
+ /** IRI uniquely identifying this verb (e.g. from adlnet.gov/expapi/verbs). */
40
+ id: string;
41
+ /** Language-map of human-readable verb labels. */
42
+ display: Record<string, string>;
43
+ }
44
+ /** Score sub-object within an xAPI Result. */
45
+ interface XAPIScore {
46
+ /** Normalized score in the range [0, 1]. */
47
+ scaled: number;
48
+ /** Raw score in the instrument's native scale. */
49
+ raw?: number;
50
+ /** Minimum possible raw score. */
51
+ min?: number;
52
+ /** Maximum possible raw score. */
53
+ max?: number;
54
+ }
55
+ /** Result information attached to an xAPI Statement. */
56
+ interface XAPIResult {
57
+ score?: XAPIScore;
58
+ /** Whether the actor's performance was successful. */
59
+ success?: boolean;
60
+ /** Whether the activity was completed. */
61
+ completion?: boolean;
62
+ /** ISO 8601 duration string representing time spent (e.g. `PT5M30S`). */
63
+ duration?: string;
64
+ /** A string representation of the learner's response. */
65
+ response?: string;
66
+ extensions?: Record<string, unknown>;
67
+ }
68
+ /** Context information attached to an xAPI Statement. */
69
+ interface XAPIContext {
70
+ /** Name of the software used to record the statement. */
71
+ platform?: string;
72
+ /** BCP 47 language tag for the activity content. */
73
+ language?: string;
74
+ extensions?: Record<string, unknown>;
75
+ }
76
+ /** A complete xAPI 1.0.3 Statement. */
77
+ interface XAPIStatement {
78
+ /** UUID v4 uniquely identifying this statement. */
79
+ id: string;
80
+ actor: XAPIActor;
81
+ verb: XAPIVerbObject;
82
+ object: XAPIObject;
83
+ result?: XAPIResult;
84
+ context?: XAPIContext;
85
+ /** ISO 8601 timestamp of when the statement occurred. */
86
+ timestamp: string;
87
+ version: '1.0.3';
88
+ }
89
+ /**
90
+ * Configuration passed to `useXAPI` to connect to an LRS endpoint.
91
+ * The `auth` field supports both HTTP Basic and Bearer token authentication.
92
+ */
93
+ interface XAPIConfig {
94
+ /** Full URL of the LRS statements endpoint. */
95
+ endpoint: string;
96
+ /** Authentication credentials for the LRS. */
97
+ auth: {
98
+ type: 'basic';
99
+ username: string;
100
+ password: string;
101
+ } | {
102
+ type: 'bearer';
103
+ token: string;
104
+ };
105
+ /** IRI identifying the activity in all statements sent by this hook instance. */
106
+ activityId: string;
107
+ actor: XAPIActor;
108
+ /** Called after all retry attempts are exhausted. */
109
+ onError?: (err: XAPIError) => void;
110
+ }
111
+ /** Error payload delivered to `XAPIConfig.onError` after retries are exhausted. */
112
+ interface XAPIError {
113
+ /** The statement that failed to send. */
114
+ statement: XAPIStatement;
115
+ /** The 1-based attempt number on which the final failure occurred. */
116
+ attempt: number;
117
+ /** HTTP status code from the LRS, if a response was received. */
118
+ statusCode?: number;
119
+ /** Human-readable error description. */
120
+ message: string;
121
+ }
122
+
123
+ /** The set of activity types supported by learning-kit. */
124
+ type ActivityType = 'multiple-choice' | 'fill-in-the-blanks';
125
+ /** Maps each ActivityType string to its corresponding data shape. */
126
+ interface ActivityDataMap {
127
+ 'multiple-choice': MultipleChoiceData;
128
+ 'fill-in-the-blanks': FillInTheBlanksData;
129
+ }
130
+ /** Union of all valid activity data shapes. */
131
+ type ActivityData = MultipleChoiceData | FillInTheBlanksData;
132
+ /**
133
+ * Optional media (image/audio/video) shown above a question or passage.
134
+ * URL-only: hosting/delivery is the consuming application's responsibility.
135
+ */
136
+ interface ActivityMedia {
137
+ /** The kind of media; selects the rendered element. `embed` → sandboxed iframe. */
138
+ type: 'image' | 'audio' | 'video' | 'embed';
139
+ /**
140
+ * Source URL. For `embed` this MUST be the provider's embeddable URL
141
+ * (e.g. `https://www.youtube.com/embed/<id>`), not the watch page.
142
+ */
143
+ url: string;
144
+ /** Alternative text. Required for `image` and `embed`; optional label for audio/video. */
145
+ alt?: string;
146
+ /** Optional WebVTT captions track URL for `audio`/`video`. */
147
+ captionsUrl?: string;
148
+ }
149
+ /**
150
+ * Optional authored "overall feedback" shown after submission, chosen by
151
+ * whether the learner passed (h5p-style overall feedback). Distinct from
152
+ * per-option feedback; either field may be omitted.
153
+ */
154
+ interface ActivityFeedback {
155
+ /** Shown when the learner passes (score ≥ pass threshold). */
156
+ correct?: string;
157
+ /** Shown when the learner does not pass. */
158
+ incorrect?: string;
159
+ }
160
+ /** A single selectable option within a Multiple Choice activity. */
161
+ interface MultipleChoiceOption {
162
+ /** Unique identifier for this option within the activity. */
163
+ id: string;
164
+ /** Display text shown to the learner. */
165
+ text: string;
166
+ /** Whether this option is part of the correct answer. */
167
+ isCorrect: boolean;
168
+ /** Optional per-option feedback shown after submission. */
169
+ feedback?: string;
170
+ }
171
+ /** Data contract for a Multiple Choice activity. */
172
+ interface MultipleChoiceData {
173
+ schemaVersion: '1.0';
174
+ type: 'multiple-choice';
175
+ /** Unique identifier for this activity. */
176
+ id: string;
177
+ /** Human-readable title used in xAPI statements and error boundaries. */
178
+ title: string;
179
+ /** The question stem presented to the learner. */
180
+ question: string;
181
+ /** `single` allows one selection; `multi` allows multiple. */
182
+ mode: 'single' | 'multi';
183
+ /** Ordered list of answer options. */
184
+ options: MultipleChoiceOption[];
185
+ /** Scoring algorithm applied when the learner submits. */
186
+ scoringStrategy: 'all-or-nothing' | 'partial';
187
+ /** Optional media shown above the question. */
188
+ media?: ActivityMedia;
189
+ /** Optional authored overall feedback shown after submission. */
190
+ feedback?: ActivityFeedback;
191
+ /** Minimum scaled score [0–1] required to pass. Defaults to 0.6 when absent. */
192
+ passThreshold?: number;
193
+ /** When true, options are shuffled deterministically per session. */
194
+ shuffle?: boolean;
195
+ /** BCP 47 language tag for the activity content. */
196
+ locale?: string;
197
+ /** IRI references to learning objectives addressed by this activity. */
198
+ learningObjectives?: string[];
199
+ /** Subjective difficulty on a 1–5 scale. */
200
+ difficultyLevel?: 1 | 2 | 3 | 4 | 5;
201
+ }
202
+ /** Configuration for a single fill-in-the-blank slot. */
203
+ interface BlankConfig {
204
+ /** Unique identifier matching the `{{blank_id}}` placeholder in the passage. */
205
+ id: string;
206
+ /** List of strings accepted as correct answers for this blank. */
207
+ acceptedAnswers: string[];
208
+ /** Whether answer matching is case-sensitive. Defaults to false. */
209
+ caseSensitive?: boolean;
210
+ /** Whether leading/trailing whitespace is stripped before matching. Defaults to true. */
211
+ trimWhitespace?: boolean;
212
+ /** Optional hint text revealed on learner request. */
213
+ hint?: string;
214
+ /** Optional feedback shown inline next to this blank after submission. */
215
+ feedback?: string;
216
+ }
217
+ /** Data contract for a Fill-in-the-Blanks activity. */
218
+ interface FillInTheBlanksData {
219
+ schemaVersion: '1.0';
220
+ type: 'fill-in-the-blanks';
221
+ /** Unique identifier for this activity. */
222
+ id: string;
223
+ /** Human-readable title used in xAPI statements and error boundaries. */
224
+ title: string;
225
+ /** Passage text containing `{{blank_id}}` placeholders. */
226
+ passage: string;
227
+ /** Configuration for each blank in the passage. */
228
+ blanks: BlankConfig[];
229
+ /** Scoring algorithm applied when the learner submits. */
230
+ scoringStrategy: 'all-or-nothing' | 'partial';
231
+ /** Optional media shown above the passage. */
232
+ media?: ActivityMedia;
233
+ /** Optional authored overall feedback shown after submission. */
234
+ feedback?: ActivityFeedback;
235
+ /** Minimum scaled score [0–1] required to pass. Defaults to 0.6 when absent. */
236
+ passThreshold?: number;
237
+ /** BCP 47 language tag for the activity content. */
238
+ locale?: string;
239
+ /** IRI references to learning objectives addressed by this activity. */
240
+ learningObjectives?: string[];
241
+ /** Subjective difficulty on a 1–5 scale. */
242
+ difficultyLevel?: 1 | 2 | 3 | 4 | 5;
243
+ }
244
+ /** Union of all learner response shapes. */
245
+ type LearnerResponse = MultipleChoiceLearnerResponse | FillInTheBlanksLearnerResponse;
246
+ /** Learner response for a Multiple Choice activity. */
247
+ interface MultipleChoiceLearnerResponse {
248
+ type: 'multiple-choice';
249
+ /** IDs of the options the learner selected. */
250
+ selectedOptionIds: string[];
251
+ }
252
+ /** Learner response for a Fill-in-the-Blanks activity. */
253
+ interface FillInTheBlanksLearnerResponse {
254
+ type: 'fill-in-the-blanks';
255
+ /** Map of blank ID to the learner's typed answer. */
256
+ answers: Record<string, string>;
257
+ }
258
+ /** Per-item scoring breakdown returned by the scoring engine. */
259
+ interface ScoringDetail {
260
+ /** ID of the option or blank this detail refers to. */
261
+ itemId: string;
262
+ /** Whether the learner's response for this item was correct. */
263
+ correct: boolean;
264
+ /** The learner's actual response for this item. */
265
+ learnerResponse: string | string[];
266
+ /** The expected correct response(s) for this item. */
267
+ correctResponse: string | string[];
268
+ /** Optional weight applied to this item's contribution to the overall score. */
269
+ weight?: number;
270
+ }
271
+ /** Full scoring result returned by the scoring engine. */
272
+ interface ScoringResult {
273
+ /** Scaled score in the range [0, 1]. */
274
+ score: number;
275
+ /** Maximum possible scaled score (always 1). */
276
+ maxScore: number;
277
+ /** Whether the score meets or exceeds the activity's pass threshold. */
278
+ passed: boolean;
279
+ /** Optional top-level feedback message, or null when absent. */
280
+ feedback: string | null;
281
+ /** Per-item scoring breakdown. */
282
+ details: ScoringDetail[];
283
+ }
284
+ /** A single validation error produced by `validateActivity`. */
285
+ interface ValidationError {
286
+ /** JSON-path-style location of the invalid field. */
287
+ path: string[];
288
+ /** Human-readable description of the validation failure. */
289
+ message: string;
290
+ /** Machine-readable error code. */
291
+ code: string;
292
+ }
293
+ /**
294
+ * Result of a `validateActivity` call.
295
+ * On success, `data` is the validated and typed activity data.
296
+ * On failure, `errors` contains one entry per violated constraint.
297
+ */
298
+ type ValidationResult<T> = {
299
+ success: true;
300
+ data: T;
301
+ } | {
302
+ success: false;
303
+ errors: ValidationError[];
304
+ };
305
+ /** The payload delivered to an activity's `onComplete` callback. */
306
+ interface ActivityResult {
307
+ /** Scaled score in the range [0, 1]. */
308
+ score: number;
309
+ /** Maximum possible scaled score (always 1). */
310
+ maxScore: number;
311
+ /** Whether the learner passed based on the activity's pass threshold. */
312
+ passed: boolean;
313
+ /** Time in milliseconds from first interaction to submission. */
314
+ timeSpent: number;
315
+ /** The xAPI statement built and (optionally) sent for this attempt. */
316
+ xapiStatement: XAPIStatement;
317
+ }
318
+ /** Fired by activity components on every discrete learner interaction. */
319
+ interface InteractionEvent {
320
+ /** The kind of interaction that occurred. */
321
+ type: 'option-selected' | 'option-deselected' | 'blank-filled' | 'hint-requested' | 'submitted';
322
+ /** ID of the activity that produced the event. */
323
+ activityId: string;
324
+ /** Unix timestamp (ms) of when the interaction occurred. */
325
+ timestamp: number;
326
+ /** Additional event-specific data. */
327
+ payload: Record<string, unknown>;
328
+ }
329
+
330
+ export type { ActivityData as A, BlankConfig as B, FillInTheBlanksData as F, InteractionEvent as I, LearnerResponse as L, MultipleChoiceData as M, ScoringDetail as S, ValidationError as V, XAPIActor as X, ActivityDataMap as a, ActivityFeedback as b, ActivityMedia as c, ActivityResult as d, ActivityType as e, FillInTheBlanksLearnerResponse as f, MultipleChoiceLearnerResponse as g, MultipleChoiceOption as h, ScoringResult as i, ValidationResult as j, XAPIConfig as k, XAPIContext as l, XAPIError as m, XAPIObject as n, XAPIResult as o, XAPIScore as p, XAPIStatement as q, XAPIVerbObject as r };