@intellectif/lk-core 0.4.0 → 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.
Files changed (54) hide show
  1. package/dist/{chunk-QOAORTA4.cjs → chunk-4JR3UXX6.cjs} +51 -2
  2. package/dist/chunk-4JR3UXX6.cjs.map +1 -0
  3. package/dist/{chunk-A6JXV5GP.js → chunk-DUQVGLQ3.js} +56 -57
  4. package/dist/chunk-DUQVGLQ3.js.map +1 -0
  5. package/dist/chunk-FCM5VJBS.cjs +187 -0
  6. package/dist/chunk-FCM5VJBS.cjs.map +1 -0
  7. package/dist/chunk-LSHDNA2T.js +187 -0
  8. package/dist/chunk-LSHDNA2T.js.map +1 -0
  9. package/dist/{chunk-5GJJHGY5.js → chunk-NUCEUU4P.js} +50 -1
  10. package/dist/chunk-NUCEUU4P.js.map +1 -0
  11. package/dist/{chunk-RUZ52Q7A.cjs → chunk-R7PDJBRX.cjs} +19 -4
  12. package/dist/chunk-R7PDJBRX.cjs.map +1 -0
  13. package/dist/{chunk-5ZAW76F3.cjs → chunk-SM5HUGYU.cjs} +59 -60
  14. package/dist/chunk-SM5HUGYU.cjs.map +1 -0
  15. package/dist/{chunk-APDLWLYD.js → chunk-WV5WQ3SZ.js} +19 -4
  16. package/dist/chunk-WV5WQ3SZ.js.map +1 -0
  17. package/dist/index-BKyZrd94.d.cts +732 -0
  18. package/dist/index-CLmXzBhB.d.ts +732 -0
  19. package/dist/index.cjs +192 -15
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +133 -3
  22. package/dist/index.d.ts +133 -3
  23. package/dist/index.js +189 -12
  24. package/dist/index.js.map +1 -1
  25. package/dist/schemas.cjs +17 -4
  26. package/dist/schemas.cjs.map +1 -1
  27. package/dist/schemas.d.cts +3 -460
  28. package/dist/schemas.d.ts +3 -460
  29. package/dist/schemas.js +16 -3
  30. package/dist/scoring.cjs +3 -4
  31. package/dist/scoring.cjs.map +1 -1
  32. package/dist/scoring.d.cts +9 -1
  33. package/dist/scoring.d.ts +9 -1
  34. package/dist/scoring.js +2 -3
  35. package/dist/xapi.cjs +5 -3
  36. package/dist/xapi.cjs.map +1 -1
  37. package/dist/xapi.d.cts +18 -1
  38. package/dist/xapi.d.ts +18 -1
  39. package/dist/xapi.js +6 -4
  40. package/package.json +3 -2
  41. package/dist/chunk-3YAVDV5F.js +0 -47
  42. package/dist/chunk-3YAVDV5F.js.map +0 -1
  43. package/dist/chunk-55O4M45K.js +0 -57
  44. package/dist/chunk-55O4M45K.js.map +0 -1
  45. package/dist/chunk-5GJJHGY5.js.map +0 -1
  46. package/dist/chunk-5ZAW76F3.cjs.map +0 -1
  47. package/dist/chunk-A6JXV5GP.js.map +0 -1
  48. package/dist/chunk-APDLWLYD.js.map +0 -1
  49. package/dist/chunk-PIMX4B4D.cjs +0 -47
  50. package/dist/chunk-PIMX4B4D.cjs.map +0 -1
  51. package/dist/chunk-QOAORTA4.cjs.map +0 -1
  52. package/dist/chunk-RUZ52Q7A.cjs.map +0 -1
  53. package/dist/chunk-YNTYWHZI.cjs +0 -57
  54. package/dist/chunk-YNTYWHZI.cjs.map +0 -1
@@ -0,0 +1,732 @@
1
+ import { A as ActivityData, c as ActivityMedia, t as ValidationResult, e as ActivityType, a as ActivityDataMap } from './activity-wkzRemHx.cjs';
2
+ import { z } from 'zod/v4';
3
+
4
+ /** What a stimulus primarily is. Drives validation and layout, never scoring. */
5
+ type StimulusKind = 'text' | 'audio' | 'video' | 'image' | 'mixed';
6
+ /**
7
+ * Shared material that several items refer to: a reading passage, a
8
+ * recording, a chart. Modelled as CONTENT — it lives inside the item group
9
+ * that carries it — so it works in a lesson quiz, a sequence and an exam
10
+ * section alike, and needs no blueprint to exist.
11
+ *
12
+ * Learner-visible by definition: the point of a stimulus is that the learner
13
+ * reads or hears it. The one exception is `transcript`, an author asset that
14
+ * `redactItemGroup` removes.
15
+ */
16
+ interface Stimulus {
17
+ id: string;
18
+ kind: StimulusKind;
19
+ /** Optional heading (a passage title). */
20
+ title?: string;
21
+ /**
22
+ * Plain-text body: the passage itself, or short instructions above media.
23
+ * Required for `text` and `mixed`.
24
+ */
25
+ body?: string;
26
+ /**
27
+ * Optional sanitised rich-HTML sidecar of `body`. Requires `body`, which is
28
+ * the accessible fallback rendered when no sanitiser is supplied.
29
+ */
30
+ bodyHtml?: string;
31
+ /** The recording, video or image. Required for `audio`, `video`, `image` and `mixed`. */
32
+ media?: ActivityMedia;
33
+ /**
34
+ * Author-only transcript of `media` — for item generation and grading,
35
+ * never shown to the learner. Removed by `redactItemGroup`.
36
+ */
37
+ transcript?: string;
38
+ /** BCP 47 language tag of the material, when it differs from the items'. */
39
+ locale?: string;
40
+ /** Source credit, shown to the learner after the material. */
41
+ attribution?: string;
42
+ }
43
+ /**
44
+ * One stimulus serving several items. Two rules, both taken from observed
45
+ * failures: a group is SHUFFLE-ATOMIC (shuffling a sequence moves the group
46
+ * as one block — interleaving two passages' questions is the defect), and its
47
+ * stimulus is presented persistently alongside every item, never behind a
48
+ * toggle the learner must reopen per question.
49
+ *
50
+ * Generic over the item type so one shape describes a group of full activity
51
+ * data (authoring, server) and a group of `redact()` projections (client)
52
+ * without a cast at every boundary.
53
+ */
54
+ interface ItemGroup<TItem = ActivityData> {
55
+ schemaVersion: '1.0';
56
+ type: 'item-group';
57
+ id: string;
58
+ title?: string;
59
+ stimulus: Stimulus;
60
+ /** The items, in authored order. Non-empty; ids unique within the group; no nested groups. */
61
+ items: TItem[];
62
+ /**
63
+ * `none` (default) keeps authored order; `within-group` shuffles the items
64
+ * among themselves under the sequence seed. Either way the group stays one
65
+ * contiguous block.
66
+ */
67
+ shuffle?: 'none' | 'within-group';
68
+ }
69
+ /** What a sequence is made of: loose activities and item groups, in authored order. */
70
+ type SequenceEntry<TItem = ActivityData> = TItem | ItemGroup<TItem>;
71
+ /** The group a presented slot belongs to. */
72
+ interface SequenceSlotGroup {
73
+ id: string;
74
+ title?: string;
75
+ stimulus: Stimulus;
76
+ /** 0-based position of this slot within the group's PRESENTED order. */
77
+ position: number;
78
+ /** Number of slots in the group. */
79
+ size: number;
80
+ }
81
+ /** One presented position in a flattened sequence. */
82
+ interface SequenceSlot<TItem = ActivityData> {
83
+ /**
84
+ * Identity of the slot within the sequence definition — unique, and stable
85
+ * under shuffling. Derived from the AUTHORED position (`"2"` for the third
86
+ * top-level entry; `"2.1"` for the second item of that entry when it is a
87
+ * group), so the same activity can appear in two entries and still be two
88
+ * slots. Feed it to `composeAssessmentScore` as `slotId`.
89
+ */
90
+ slotId: string;
91
+ /** 0-based PRESENTED position, after shuffling. */
92
+ index: number;
93
+ activity: TItem;
94
+ /** Present when the slot comes from an item group. */
95
+ group?: SequenceSlotGroup;
96
+ }
97
+
98
+ /**
99
+ * Optional authored "overall feedback" shown after submission, selected by
100
+ * whether the learner passed (h5p-style overall feedback). Both fields are
101
+ * optional; non-empty when present.
102
+ */
103
+ declare const FeedbackSchema: z.ZodObject<{
104
+ correct: z.ZodOptional<z.ZodString>;
105
+ incorrect: z.ZodOptional<z.ZodString>;
106
+ }, z.core.$loose>;
107
+
108
+ /**
109
+ * Zod schema for a `TextMatchPolicy` — the opt-in matching tolerances a blank
110
+ * may declare. Every default reproduces the v1 trim + case-fold semantics.
111
+ */
112
+ declare const TextMatchPolicySchema: z.ZodObject<{
113
+ caseSensitive: z.ZodOptional<z.ZodBoolean>;
114
+ trim: z.ZodOptional<z.ZodBoolean>;
115
+ normalize: z.ZodOptional<z.ZodEnum<{
116
+ none: "none";
117
+ NFC: "NFC";
118
+ NFKC: "NFKC";
119
+ }>>;
120
+ foldDiacritics: z.ZodOptional<z.ZodBoolean>;
121
+ collapseInnerWhitespace: z.ZodOptional<z.ZodBoolean>;
122
+ ignorePunctuation: z.ZodOptional<z.ZodBoolean>;
123
+ levenshtein: z.ZodOptional<z.ZodNumber>;
124
+ locale: z.ZodOptional<z.ZodString>;
125
+ }, z.core.$loose>;
126
+ /**
127
+ * Zod schema for a single fill-in-the-blank slot configuration. Loose:
128
+ * unknown keys are preserved through validation. Accepted answers must
129
+ * contain non-whitespace characters — a whitespace-only accepted answer
130
+ * normalizes to the empty string and would mark an empty response correct.
131
+ */
132
+ declare const BlankConfigSchema: z.ZodObject<{
133
+ id: z.ZodString;
134
+ acceptedAnswers: z.ZodArray<z.ZodString>;
135
+ caseSensitive: z.ZodOptional<z.ZodBoolean>;
136
+ trimWhitespace: z.ZodOptional<z.ZodBoolean>;
137
+ match: z.ZodOptional<z.ZodObject<{
138
+ caseSensitive: z.ZodOptional<z.ZodBoolean>;
139
+ trim: z.ZodOptional<z.ZodBoolean>;
140
+ normalize: z.ZodOptional<z.ZodEnum<{
141
+ none: "none";
142
+ NFC: "NFC";
143
+ NFKC: "NFKC";
144
+ }>>;
145
+ foldDiacritics: z.ZodOptional<z.ZodBoolean>;
146
+ collapseInnerWhitespace: z.ZodOptional<z.ZodBoolean>;
147
+ ignorePunctuation: z.ZodOptional<z.ZodBoolean>;
148
+ levenshtein: z.ZodOptional<z.ZodNumber>;
149
+ locale: z.ZodOptional<z.ZodString>;
150
+ }, z.core.$loose>>;
151
+ hint: z.ZodOptional<z.ZodString>;
152
+ feedback: z.ZodOptional<z.ZodString>;
153
+ }, z.core.$loose>;
154
+ /**
155
+ * Zod schema validating the full Fill-in-the-Blanks activity data contract.
156
+ * Loose at every level: unknown keys are preserved, never stripped (B7).
157
+ *
158
+ * The refinement enforces a true one-to-one correspondence between `{{id}}`
159
+ * placeholders and `blanks[].id`: every blank id appears EXACTLY ONCE in the
160
+ * passage and exactly once in `blanks[]`. (The previous set-based check let
161
+ * duplicate placeholders and duplicate blank configs through, corrupting the
162
+ * partial-score denominator and per-item details.)
163
+ */
164
+ declare const FillInTheBlanksDataSchema: z.ZodObject<{
165
+ schemaVersion: z.ZodLiteral<"1.0">;
166
+ type: z.ZodLiteral<"fill-in-the-blanks">;
167
+ id: z.ZodString;
168
+ title: z.ZodString;
169
+ passage: z.ZodString;
170
+ passageHtml: z.ZodOptional<z.ZodString>;
171
+ blanks: z.ZodArray<z.ZodObject<{
172
+ id: z.ZodString;
173
+ acceptedAnswers: z.ZodArray<z.ZodString>;
174
+ caseSensitive: z.ZodOptional<z.ZodBoolean>;
175
+ trimWhitespace: z.ZodOptional<z.ZodBoolean>;
176
+ match: z.ZodOptional<z.ZodObject<{
177
+ caseSensitive: z.ZodOptional<z.ZodBoolean>;
178
+ trim: z.ZodOptional<z.ZodBoolean>;
179
+ normalize: z.ZodOptional<z.ZodEnum<{
180
+ none: "none";
181
+ NFC: "NFC";
182
+ NFKC: "NFKC";
183
+ }>>;
184
+ foldDiacritics: z.ZodOptional<z.ZodBoolean>;
185
+ collapseInnerWhitespace: z.ZodOptional<z.ZodBoolean>;
186
+ ignorePunctuation: z.ZodOptional<z.ZodBoolean>;
187
+ levenshtein: z.ZodOptional<z.ZodNumber>;
188
+ locale: z.ZodOptional<z.ZodString>;
189
+ }, z.core.$loose>>;
190
+ hint: z.ZodOptional<z.ZodString>;
191
+ feedback: z.ZodOptional<z.ZodString>;
192
+ }, z.core.$loose>>;
193
+ scoringStrategy: z.ZodEnum<{
194
+ "all-or-nothing": "all-or-nothing";
195
+ partial: "partial";
196
+ }>;
197
+ media: z.ZodOptional<z.ZodObject<{
198
+ type: z.ZodEnum<{
199
+ image: "image";
200
+ audio: "audio";
201
+ video: "video";
202
+ embed: "embed";
203
+ }>;
204
+ url: z.ZodUnion<readonly [z.ZodURL, z.ZodString]>;
205
+ alt: z.ZodOptional<z.ZodString>;
206
+ captionsUrl: z.ZodOptional<z.ZodUnion<readonly [z.ZodURL, z.ZodString]>>;
207
+ }, z.core.$loose>>;
208
+ feedback: z.ZodOptional<z.ZodObject<{
209
+ correct: z.ZodOptional<z.ZodString>;
210
+ incorrect: z.ZodOptional<z.ZodString>;
211
+ }, z.core.$loose>>;
212
+ passThreshold: z.ZodOptional<z.ZodNumber>;
213
+ locale: z.ZodOptional<z.ZodString>;
214
+ learningObjectives: z.ZodOptional<z.ZodArray<z.ZodString>>;
215
+ difficultyLevel: z.ZodOptional<z.ZodLiteral<1 | 2 | 3 | 4 | 5>>;
216
+ }, z.core.$loose>;
217
+
218
+ /**
219
+ * Zod schema for a {@link Stimulus}. Loose: unknown keys preserved.
220
+ *
221
+ * The semantic guards exist because a stimulus that does not carry what its
222
+ * `kind` promises is an authoring error that must not reach an exam: an
223
+ * "audio" stimulus with no recording renders as a blank panel above six
224
+ * listening questions. Unrepresentable in JSON Schema; dropped from
225
+ * `stimulusJsonSchema` by design.
226
+ */
227
+ declare const StimulusSchema: z.ZodObject<{
228
+ id: z.ZodString;
229
+ kind: z.ZodEnum<{
230
+ image: "image";
231
+ audio: "audio";
232
+ video: "video";
233
+ text: "text";
234
+ mixed: "mixed";
235
+ }>;
236
+ title: z.ZodOptional<z.ZodString>;
237
+ body: z.ZodOptional<z.ZodString>;
238
+ bodyHtml: z.ZodOptional<z.ZodString>;
239
+ media: z.ZodOptional<z.ZodObject<{
240
+ type: z.ZodEnum<{
241
+ image: "image";
242
+ audio: "audio";
243
+ video: "video";
244
+ embed: "embed";
245
+ }>;
246
+ url: z.ZodUnion<readonly [z.ZodURL, z.ZodString]>;
247
+ alt: z.ZodOptional<z.ZodString>;
248
+ captionsUrl: z.ZodOptional<z.ZodUnion<readonly [z.ZodURL, z.ZodString]>>;
249
+ }, z.core.$loose>>;
250
+ transcript: z.ZodOptional<z.ZodString>;
251
+ locale: z.ZodOptional<z.ZodString>;
252
+ attribution: z.ZodOptional<z.ZodString>;
253
+ }, z.core.$loose>;
254
+ /**
255
+ * Zod schema for an {@link ItemGroup} CONTAINER. Loose: unknown keys
256
+ * preserved. Validates the group's own fields and the stimulus in full, and
257
+ * each item only structurally (`type` and `id`); use {@link validateItemGroup}
258
+ * to validate the items against their registered schemas as well.
259
+ */
260
+ declare const ItemGroupSchema: z.ZodObject<{
261
+ schemaVersion: z.ZodLiteral<"1.0">;
262
+ type: z.ZodLiteral<"item-group">;
263
+ id: z.ZodString;
264
+ title: z.ZodOptional<z.ZodString>;
265
+ stimulus: z.ZodObject<{
266
+ id: z.ZodString;
267
+ kind: z.ZodEnum<{
268
+ image: "image";
269
+ audio: "audio";
270
+ video: "video";
271
+ text: "text";
272
+ mixed: "mixed";
273
+ }>;
274
+ title: z.ZodOptional<z.ZodString>;
275
+ body: z.ZodOptional<z.ZodString>;
276
+ bodyHtml: z.ZodOptional<z.ZodString>;
277
+ media: z.ZodOptional<z.ZodObject<{
278
+ type: z.ZodEnum<{
279
+ image: "image";
280
+ audio: "audio";
281
+ video: "video";
282
+ embed: "embed";
283
+ }>;
284
+ url: z.ZodUnion<readonly [z.ZodURL, z.ZodString]>;
285
+ alt: z.ZodOptional<z.ZodString>;
286
+ captionsUrl: z.ZodOptional<z.ZodUnion<readonly [z.ZodURL, z.ZodString]>>;
287
+ }, z.core.$loose>>;
288
+ transcript: z.ZodOptional<z.ZodString>;
289
+ locale: z.ZodOptional<z.ZodString>;
290
+ attribution: z.ZodOptional<z.ZodString>;
291
+ }, z.core.$loose>;
292
+ items: z.ZodArray<z.ZodObject<{
293
+ type: z.ZodString;
294
+ id: z.ZodString;
295
+ }, z.core.$loose>>;
296
+ shuffle: z.ZodOptional<z.ZodEnum<{
297
+ none: "none";
298
+ "within-group": "within-group";
299
+ }>>;
300
+ }, z.core.$loose>;
301
+ /** Strict learner-safe stimulus: everything but the author-only `transcript`. */
302
+ declare const RedactedStimulusSchema: z.ZodObject<{
303
+ id: z.ZodString;
304
+ kind: z.ZodEnum<{
305
+ image: "image";
306
+ audio: "audio";
307
+ video: "video";
308
+ text: "text";
309
+ mixed: "mixed";
310
+ }>;
311
+ title: z.ZodOptional<z.ZodString>;
312
+ body: z.ZodOptional<z.ZodString>;
313
+ bodyHtml: z.ZodOptional<z.ZodString>;
314
+ media: z.ZodOptional<z.ZodObject<{
315
+ type: z.ZodEnum<{
316
+ image: "image";
317
+ audio: "audio";
318
+ video: "video";
319
+ embed: "embed";
320
+ }>;
321
+ url: z.ZodUnion<readonly [z.ZodURL, z.ZodString]>;
322
+ alt: z.ZodOptional<z.ZodString>;
323
+ captionsUrl: z.ZodOptional<z.ZodUnion<readonly [z.ZodURL, z.ZodString]>>;
324
+ }, z.core.$loose>>;
325
+ locale: z.ZodOptional<z.ZodString>;
326
+ attribution: z.ZodOptional<z.ZodString>;
327
+ }, z.core.$strict>;
328
+ /**
329
+ * Strict learner-safe item group. Items are left opaque here — each is proven
330
+ * learner-safe by `assertRedacted` against its OWN type's redacted schema,
331
+ * which is the only place that knowledge lives.
332
+ */
333
+ declare const RedactedItemGroupSchema: z.ZodObject<{
334
+ redacted: z.ZodLiteral<true>;
335
+ schemaVersion: z.ZodLiteral<"1.0">;
336
+ type: z.ZodLiteral<"item-group">;
337
+ id: z.ZodString;
338
+ title: z.ZodOptional<z.ZodString>;
339
+ stimulus: z.ZodObject<{
340
+ id: z.ZodString;
341
+ kind: z.ZodEnum<{
342
+ image: "image";
343
+ audio: "audio";
344
+ video: "video";
345
+ text: "text";
346
+ mixed: "mixed";
347
+ }>;
348
+ title: z.ZodOptional<z.ZodString>;
349
+ body: z.ZodOptional<z.ZodString>;
350
+ bodyHtml: z.ZodOptional<z.ZodString>;
351
+ media: z.ZodOptional<z.ZodObject<{
352
+ type: z.ZodEnum<{
353
+ image: "image";
354
+ audio: "audio";
355
+ video: "video";
356
+ embed: "embed";
357
+ }>;
358
+ url: z.ZodUnion<readonly [z.ZodURL, z.ZodString]>;
359
+ alt: z.ZodOptional<z.ZodString>;
360
+ captionsUrl: z.ZodOptional<z.ZodUnion<readonly [z.ZodURL, z.ZodString]>>;
361
+ }, z.core.$loose>>;
362
+ locale: z.ZodOptional<z.ZodString>;
363
+ attribution: z.ZodOptional<z.ZodString>;
364
+ }, z.core.$strict>;
365
+ items: z.ZodArray<z.ZodUnknown>;
366
+ shuffle: z.ZodOptional<z.ZodEnum<{
367
+ none: "none";
368
+ "within-group": "within-group";
369
+ }>>;
370
+ }, z.core.$strict>;
371
+ /**
372
+ * Validates an item group in full: the container and stimulus against
373
+ * {@link ItemGroupSchema}, then every item against the schema registered for
374
+ * its `type`. Errors from items are reported at `items.<index>.…`.
375
+ *
376
+ * Unlike `validateActivity`, an item whose type is not registered is REPORTED
377
+ * (code `unknown_activity_type`) rather than thrown: a group is validated as
378
+ * a whole, and an author fixing a six-item group wants every problem listed,
379
+ * not the first one that happened to throw.
380
+ */
381
+ declare function validateItemGroup(data: unknown): ValidationResult<ItemGroup>;
382
+
383
+ /**
384
+ * JSON Schema (Draft 7) for a `Stimulus`. Structural contract only — the
385
+ * kind/media/body consistency guards are Zod-only.
386
+ */
387
+ declare const stimulusJsonSchema: z.core.JSONSchema.JSONSchema;
388
+ /**
389
+ * JSON Schema (Draft 7) for an `ItemGroup` CONTAINER. Items appear as objects
390
+ * with `type` and `id` only; each item's own contract is `jsonSchemaFor(type)`.
391
+ * For an AI generation pipeline, ask for the group and each item separately
392
+ * rather than a single nested schema — that keeps the per-type schema the
393
+ * registry's, not a copy.
394
+ */
395
+ declare const itemGroupJsonSchema: z.core.JSONSchema.JSONSchema;
396
+ /**
397
+ * JSON Schema (Draft 7) representation of the Multiple Choice activity data
398
+ * contract, generated natively by Zod 4. Draft 7 is mandated by Requirement
399
+ * 2.2 for the widest AI-prompt / OpenAPI tooling compatibility. The semantic
400
+ * `.refine()` guards are not representable in JSON Schema and are
401
+ * intentionally omitted — the export captures the *structural* contract only.
402
+ * Since v0.3 the source schemas are loose, so these no longer emit
403
+ * `additionalProperties: false` — the JSON Schema and `validateActivity` now
404
+ * agree on unknown-key handling.
405
+ */
406
+ declare const multipleChoiceJsonSchema: z.core.JSONSchema.JSONSchema;
407
+ /**
408
+ * JSON Schema (Draft 7) representation of the Fill-in-the-Blanks activity data
409
+ * contract, generated natively by Zod 4. Structural contract only (semantic
410
+ * `.refine()` guards are Zod-only and not representable in JSON Schema).
411
+ */
412
+ declare const fillInTheBlanksJsonSchema: z.core.JSONSchema.JSONSchema;
413
+ /**
414
+ * JSON Schema (Draft 7) representation of the Written Response activity data
415
+ * contract. Structural contract only.
416
+ */
417
+ declare const writtenResponseJsonSchema: z.core.JSONSchema.JSONSchema;
418
+ /**
419
+ * Derives the JSON Schema (Draft 7) for any REGISTERED activity type — the
420
+ * live, registry-backed replacement for the static per-type exports above,
421
+ * and the building block for AI generation pipelines (R6.1): pass the result
422
+ * as a structured-output schema so a model can only emit valid items.
423
+ *
424
+ * @throws UnknownActivityTypeError when `type` has no registered descriptor.
425
+ */
426
+ declare function jsonSchemaFor(type: string): Record<string, unknown>;
427
+
428
+ /**
429
+ * Optional media attached to an activity, rendered above the question or
430
+ * passage — e.g. a recording to listen to, or an embedded video to watch
431
+ * before answering. URL-only by design: hosting/delivery (S3/CDN, or the
432
+ * provider's own embed for `embed`) is the consuming app's responsibility
433
+ * (see requirements "Non-Goals and Shared Responsibility").
434
+ *
435
+ * - `image`: rendered as `<img>` — `alt` is REQUIRED (WCAG 1.1.1 / Req 14.5).
436
+ * - `audio` / `video`: rendered with native controls; `alt` is an optional
437
+ * accessible label; `captionsUrl` points at a WebVTT `<track>`. `url` must
438
+ * be a direct media file (NOT a YouTube/Vimeo page — use `embed` for those).
439
+ * - `embed`: rendered as a sandboxed `<iframe>` for provider players
440
+ * (YouTube/Vimeo/etc.). `url` MUST be the provider's *embeddable* URL
441
+ * (e.g. `https://www.youtube.com/embed/<id>`). `alt` is REQUIRED and used
442
+ * as the iframe's accessible `title` (WCAG 4.1.2 / 2.4.1).
443
+ */
444
+ /**
445
+ * Media URL policy (security-reviewed): absolute URLs must use `https:`,
446
+ * `http:`, `data:`, or `blob:`; root-relative paths (`/media/x.mp3`) are
447
+ * allowed for same-origin hosting. Everything else — notably `javascript:`,
448
+ * `file:`, `ftp:` — is rejected: these URLs land in `src` attributes
449
+ * (including an iframe for `embed`), so an unvetted scheme is a stored-XSS
450
+ * vector in every consuming app.
451
+ */
452
+ declare const MediaUrlSchema: z.ZodUnion<readonly [z.ZodURL, z.ZodString]>;
453
+ declare const MediaSchema: z.ZodObject<{
454
+ type: z.ZodEnum<{
455
+ image: "image";
456
+ audio: "audio";
457
+ video: "video";
458
+ embed: "embed";
459
+ }>;
460
+ url: z.ZodUnion<readonly [z.ZodURL, z.ZodString]>;
461
+ alt: z.ZodOptional<z.ZodString>;
462
+ captionsUrl: z.ZodOptional<z.ZodUnion<readonly [z.ZodURL, z.ZodString]>>;
463
+ }, z.core.$loose>;
464
+
465
+ /**
466
+ * Zod schema for a single Multiple Choice option. Loose: unknown keys are
467
+ * preserved through validation (forward-compat / consumer sidecars — B7).
468
+ */
469
+ declare const MultipleChoiceOptionSchema: z.ZodObject<{
470
+ id: z.ZodString;
471
+ text: z.ZodString;
472
+ isCorrect: z.ZodBoolean;
473
+ feedback: z.ZodOptional<z.ZodString>;
474
+ }, z.core.$loose>;
475
+ /**
476
+ * Zod schema validating the full Multiple Choice activity data contract.
477
+ * Loose at every level: unknown keys are preserved, never stripped, so a
478
+ * v0.3 runtime reading a future payload (or a consumer sidecar field) does
479
+ * not silently delete data.
480
+ *
481
+ * Semantic guards: (1) at least one option must be correct, otherwise the
482
+ * activity can never be answered correctly; (2) `mode: 'single'` must have
483
+ * exactly one correct option — multiple correct options under single-select
484
+ * make `showCorrectAnswers` and the xAPI correct-response ambiguous and mask
485
+ * authoring errors; (3) option ids must be unique — the scorer looks options
486
+ * up by id, so a duplicate id makes one option unscoreable. All are
487
+ * unrepresentable in JSON Schema and are dropped from `toJSONSchema` output
488
+ * by design.
489
+ */
490
+ declare const MultipleChoiceDataSchema: z.ZodObject<{
491
+ schemaVersion: z.ZodLiteral<"1.0">;
492
+ type: z.ZodLiteral<"multiple-choice">;
493
+ id: z.ZodString;
494
+ title: z.ZodString;
495
+ question: z.ZodString;
496
+ questionHtml: z.ZodOptional<z.ZodString>;
497
+ mode: z.ZodEnum<{
498
+ single: "single";
499
+ multi: "multi";
500
+ }>;
501
+ options: z.ZodArray<z.ZodObject<{
502
+ id: z.ZodString;
503
+ text: z.ZodString;
504
+ isCorrect: z.ZodBoolean;
505
+ feedback: z.ZodOptional<z.ZodString>;
506
+ }, z.core.$loose>>;
507
+ scoringStrategy: z.ZodEnum<{
508
+ "all-or-nothing": "all-or-nothing";
509
+ partial: "partial";
510
+ }>;
511
+ media: z.ZodOptional<z.ZodObject<{
512
+ type: z.ZodEnum<{
513
+ image: "image";
514
+ audio: "audio";
515
+ video: "video";
516
+ embed: "embed";
517
+ }>;
518
+ url: z.ZodUnion<readonly [z.ZodURL, z.ZodString]>;
519
+ alt: z.ZodOptional<z.ZodString>;
520
+ captionsUrl: z.ZodOptional<z.ZodUnion<readonly [z.ZodURL, z.ZodString]>>;
521
+ }, z.core.$loose>>;
522
+ feedback: z.ZodOptional<z.ZodObject<{
523
+ correct: z.ZodOptional<z.ZodString>;
524
+ incorrect: z.ZodOptional<z.ZodString>;
525
+ }, z.core.$loose>>;
526
+ passThreshold: z.ZodOptional<z.ZodNumber>;
527
+ shuffle: z.ZodOptional<z.ZodBoolean>;
528
+ locale: z.ZodOptional<z.ZodString>;
529
+ learningObjectives: z.ZodOptional<z.ZodArray<z.ZodString>>;
530
+ difficultyLevel: z.ZodOptional<z.ZodLiteral<1 | 2 | 3 | 4 | 5>>;
531
+ }, z.core.$loose>;
532
+
533
+ /** A redacted Multiple Choice option: id and display text only — no `isCorrect`, no feedback. */
534
+ declare const RedactedMultipleChoiceOptionSchema: z.ZodObject<{
535
+ id: z.ZodString;
536
+ text: z.ZodString;
537
+ }, z.core.$strict>;
538
+ /**
539
+ * Redacted Multiple Choice data: renderable (question, mode, options to pick
540
+ * from) with the answer key, per-option feedback, overall feedback, and the
541
+ * scoring strategy removed. `scoringStrategy` is answer-key by design: MC
542
+ * `partial` carries a wrong-selection penalty `all-or-nothing` does not, so
543
+ * knowing the strategy tells a learner whether guessing is free.
544
+ */
545
+ declare const RedactedMultipleChoiceDataSchema: z.ZodObject<{
546
+ type: z.ZodLiteral<"multiple-choice">;
547
+ question: z.ZodString;
548
+ questionHtml: z.ZodOptional<z.ZodString>;
549
+ mode: z.ZodEnum<{
550
+ single: "single";
551
+ multi: "multi";
552
+ }>;
553
+ options: z.ZodArray<z.ZodObject<{
554
+ id: z.ZodString;
555
+ text: z.ZodString;
556
+ }, z.core.$strict>>;
557
+ shuffle: z.ZodOptional<z.ZodBoolean>;
558
+ /** Marker distinguishing a redacted projection from full activity data. */
559
+ redacted: z.ZodLiteral<true>;
560
+ schemaVersion: z.ZodLiteral<"1.0">;
561
+ id: z.ZodString;
562
+ title: z.ZodString;
563
+ media: z.ZodOptional<z.ZodObject<{
564
+ type: z.ZodEnum<{
565
+ image: "image";
566
+ audio: "audio";
567
+ video: "video";
568
+ embed: "embed";
569
+ }>;
570
+ url: z.ZodUnion<readonly [z.ZodURL, z.ZodString]>;
571
+ alt: z.ZodOptional<z.ZodString>;
572
+ captionsUrl: z.ZodOptional<z.ZodUnion<readonly [z.ZodURL, z.ZodString]>>;
573
+ }, z.core.$loose>>;
574
+ passThreshold: z.ZodOptional<z.ZodNumber>;
575
+ locale: z.ZodOptional<z.ZodString>;
576
+ learningObjectives: z.ZodOptional<z.ZodArray<z.ZodString>>;
577
+ difficultyLevel: z.ZodOptional<z.ZodLiteral<1 | 2 | 3 | 4 | 5>>;
578
+ }, z.core.$strict>;
579
+ /** A redacted blank: id and hint only — no accepted answers, no matching rules, no feedback. */
580
+ declare const RedactedBlankConfigSchema: z.ZodObject<{
581
+ id: z.ZodString;
582
+ hint: z.ZodOptional<z.ZodString>;
583
+ }, z.core.$strict>;
584
+ /** Redacted Fill-in-the-Blanks data: passage and blank slots, key removed. */
585
+ declare const RedactedFillInTheBlanksDataSchema: z.ZodObject<{
586
+ type: z.ZodLiteral<"fill-in-the-blanks">;
587
+ passage: z.ZodString;
588
+ passageHtml: z.ZodOptional<z.ZodString>;
589
+ blanks: z.ZodArray<z.ZodObject<{
590
+ id: z.ZodString;
591
+ hint: z.ZodOptional<z.ZodString>;
592
+ }, z.core.$strict>>;
593
+ /** Marker distinguishing a redacted projection from full activity data. */
594
+ redacted: z.ZodLiteral<true>;
595
+ schemaVersion: z.ZodLiteral<"1.0">;
596
+ id: z.ZodString;
597
+ title: z.ZodString;
598
+ media: z.ZodOptional<z.ZodObject<{
599
+ type: z.ZodEnum<{
600
+ image: "image";
601
+ audio: "audio";
602
+ video: "video";
603
+ embed: "embed";
604
+ }>;
605
+ url: z.ZodUnion<readonly [z.ZodURL, z.ZodString]>;
606
+ alt: z.ZodOptional<z.ZodString>;
607
+ captionsUrl: z.ZodOptional<z.ZodUnion<readonly [z.ZodURL, z.ZodString]>>;
608
+ }, z.core.$loose>>;
609
+ passThreshold: z.ZodOptional<z.ZodNumber>;
610
+ locale: z.ZodOptional<z.ZodString>;
611
+ learningObjectives: z.ZodOptional<z.ZodArray<z.ZodString>>;
612
+ difficultyLevel: z.ZodOptional<z.ZodLiteral<1 | 2 | 3 | 4 | 5>>;
613
+ }, z.core.$strict>;
614
+ /**
615
+ * Redacted Written Response data: the prompt, word bounds and rubric are
616
+ * learner-visible (a rubric tells the learner what they are graded on);
617
+ * authored pass/fail feedback is removed until the grade exists.
618
+ */
619
+ declare const RedactedWrittenResponseDataSchema: z.ZodObject<{
620
+ type: z.ZodLiteral<"written-response">;
621
+ prompt: z.ZodString;
622
+ promptHtml: z.ZodOptional<z.ZodString>;
623
+ minWords: z.ZodNumber;
624
+ maxWords: z.ZodNumber;
625
+ rubric: z.ZodOptional<z.ZodObject<{
626
+ label: z.ZodOptional<z.ZodString>;
627
+ criteria: z.ZodArray<z.ZodObject<{
628
+ name: z.ZodString;
629
+ description: z.ZodOptional<z.ZodString>;
630
+ weight: z.ZodNumber;
631
+ }, z.core.$loose>>;
632
+ }, z.core.$loose>>;
633
+ languageTarget: z.ZodOptional<z.ZodString>;
634
+ /** Marker distinguishing a redacted projection from full activity data. */
635
+ redacted: z.ZodLiteral<true>;
636
+ schemaVersion: z.ZodLiteral<"1.0">;
637
+ id: z.ZodString;
638
+ title: z.ZodString;
639
+ media: z.ZodOptional<z.ZodObject<{
640
+ type: z.ZodEnum<{
641
+ image: "image";
642
+ audio: "audio";
643
+ video: "video";
644
+ embed: "embed";
645
+ }>;
646
+ url: z.ZodUnion<readonly [z.ZodURL, z.ZodString]>;
647
+ alt: z.ZodOptional<z.ZodString>;
648
+ captionsUrl: z.ZodOptional<z.ZodUnion<readonly [z.ZodURL, z.ZodString]>>;
649
+ }, z.core.$loose>>;
650
+ passThreshold: z.ZodOptional<z.ZodNumber>;
651
+ locale: z.ZodOptional<z.ZodString>;
652
+ learningObjectives: z.ZodOptional<z.ZodArray<z.ZodString>>;
653
+ difficultyLevel: z.ZodOptional<z.ZodLiteral<1 | 2 | 3 | 4 | 5>>;
654
+ }, z.core.$strict>;
655
+
656
+ /** Zod schema for a single rubric criterion. Loose: unknown keys preserved. */
657
+ declare const WrittenResponseRubricCriterionSchema: z.ZodObject<{
658
+ name: z.ZodString;
659
+ description: z.ZodOptional<z.ZodString>;
660
+ weight: z.ZodNumber;
661
+ }, z.core.$loose>;
662
+ /** Zod schema for a written-response grading rubric. Loose: unknown keys preserved. */
663
+ declare const WrittenResponseRubricSchema: z.ZodObject<{
664
+ label: z.ZodOptional<z.ZodString>;
665
+ criteria: z.ZodArray<z.ZodObject<{
666
+ name: z.ZodString;
667
+ description: z.ZodOptional<z.ZodString>;
668
+ weight: z.ZodNumber;
669
+ }, z.core.$loose>>;
670
+ }, z.core.$loose>;
671
+ /**
672
+ * Zod schema validating the Written Response activity data contract (Req 22).
673
+ *
674
+ * Wire-format constraints (Req 22.9): field names are locked for
675
+ * byte-compatibility with consumer-stored rows, and the schema is loose at
676
+ * EVERY level (Req 22.5) — unknown top-level keys, `promptHtml`, `rubric`
677
+ * sidecars and any future fields survive `validateActivity` verbatim.
678
+ */
679
+ declare const WrittenResponseDataSchema: z.ZodObject<{
680
+ schemaVersion: z.ZodLiteral<"1.0">;
681
+ type: z.ZodLiteral<"written-response">;
682
+ id: z.ZodString;
683
+ title: z.ZodString;
684
+ prompt: z.ZodString;
685
+ promptHtml: z.ZodOptional<z.ZodString>;
686
+ minWords: z.ZodNumber;
687
+ maxWords: z.ZodNumber;
688
+ rubric: z.ZodOptional<z.ZodObject<{
689
+ label: z.ZodOptional<z.ZodString>;
690
+ criteria: z.ZodArray<z.ZodObject<{
691
+ name: z.ZodString;
692
+ description: z.ZodOptional<z.ZodString>;
693
+ weight: z.ZodNumber;
694
+ }, z.core.$loose>>;
695
+ }, z.core.$loose>>;
696
+ languageTarget: z.ZodOptional<z.ZodString>;
697
+ media: z.ZodOptional<z.ZodObject<{
698
+ type: z.ZodEnum<{
699
+ image: "image";
700
+ audio: "audio";
701
+ video: "video";
702
+ embed: "embed";
703
+ }>;
704
+ url: z.ZodUnion<readonly [z.ZodURL, z.ZodString]>;
705
+ alt: z.ZodOptional<z.ZodString>;
706
+ captionsUrl: z.ZodOptional<z.ZodUnion<readonly [z.ZodURL, z.ZodString]>>;
707
+ }, z.core.$loose>>;
708
+ feedback: z.ZodOptional<z.ZodObject<{
709
+ correct: z.ZodOptional<z.ZodString>;
710
+ incorrect: z.ZodOptional<z.ZodString>;
711
+ }, z.core.$loose>>;
712
+ passThreshold: z.ZodOptional<z.ZodNumber>;
713
+ locale: z.ZodOptional<z.ZodString>;
714
+ learningObjectives: z.ZodOptional<z.ZodArray<z.ZodString>>;
715
+ difficultyLevel: z.ZodOptional<z.ZodLiteral<1 | 2 | 3 | 4 | 5>>;
716
+ }, z.core.$loose>;
717
+
718
+ /**
719
+ * Validates raw activity data against the schema registered for the given
720
+ * activity type (built-in or consumer-registered via `registerActivityType`).
721
+ *
722
+ * Unknown keys are PRESERVED, not stripped: every built-in schema is loose,
723
+ * so consumer sidecar fields and forward-version fields survive validation
724
+ * verbatim in the returned `data`.
725
+ *
726
+ * @returns `{ success: true, data }` with the typed, validated data, or
727
+ * `{ success: false, errors }` with one entry per failed constraint.
728
+ * @throws UnknownActivityTypeError when `type` has no registered descriptor.
729
+ */
730
+ declare function validateActivity<T extends ActivityType>(type: T, data: unknown): ValidationResult<ActivityDataMap[T]>;
731
+
732
+ export { BlankConfigSchema as B, FeedbackSchema as F, type ItemGroup as I, MediaSchema as M, RedactedBlankConfigSchema as R, type SequenceEntry as S, TextMatchPolicySchema as T, WrittenResponseDataSchema as W, FillInTheBlanksDataSchema as a, ItemGroupSchema as b, MediaUrlSchema as c, MultipleChoiceDataSchema as d, MultipleChoiceOptionSchema as e, RedactedFillInTheBlanksDataSchema as f, RedactedItemGroupSchema as g, RedactedMultipleChoiceDataSchema as h, RedactedMultipleChoiceOptionSchema as i, RedactedStimulusSchema as j, RedactedWrittenResponseDataSchema as k, type SequenceSlot as l, type SequenceSlotGroup as m, type Stimulus as n, type StimulusKind as o, StimulusSchema as p, WrittenResponseRubricCriterionSchema as q, WrittenResponseRubricSchema as r, fillInTheBlanksJsonSchema as s, itemGroupJsonSchema as t, jsonSchemaFor as u, multipleChoiceJsonSchema as v, stimulusJsonSchema as w, validateActivity as x, validateItemGroup as y, writtenResponseJsonSchema as z };