@intellectif/lk-core 0.8.0 → 0.8.1

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 (36) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/{chunk-H7L57ITR.js → chunk-2M76F32Y.js} +2 -2
  3. package/dist/{chunk-64NBQCRE.cjs → chunk-7ACNBDSF.cjs} +4 -4
  4. package/dist/{chunk-64NBQCRE.cjs.map → chunk-7ACNBDSF.cjs.map} +1 -1
  5. package/dist/{chunk-RHJ5VNFB.cjs → chunk-FKH4YT5X.cjs} +41 -7
  6. package/dist/chunk-FKH4YT5X.cjs.map +1 -0
  7. package/dist/{chunk-JFSZTWHL.cjs → chunk-FVLKIL6W.cjs} +12 -12
  8. package/dist/{chunk-JFSZTWHL.cjs.map → chunk-FVLKIL6W.cjs.map} +1 -1
  9. package/dist/{chunk-OJXTXOUA.js → chunk-HCOWSGEZ.js} +41 -7
  10. package/dist/chunk-HCOWSGEZ.js.map +1 -0
  11. package/dist/{chunk-HTMRJ2SS.js → chunk-LNA33IK7.js} +2 -2
  12. package/dist/{chunk-ZBCKQBAB.js → chunk-NHGXOOZ2.js} +2 -2
  13. package/dist/{chunk-4VH7RUX6.cjs → chunk-YJZY5TPY.cjs} +8 -8
  14. package/dist/{chunk-4VH7RUX6.cjs.map → chunk-YJZY5TPY.cjs.map} +1 -1
  15. package/dist/{index-Kc53zVgY.d.ts → index-BvrA8nIV.d.ts} +2 -2
  16. package/dist/{index-BxW_Ho_t.d.cts → index-gIN564mV.d.cts} +2 -2
  17. package/dist/index.cjs +27 -24
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +2 -2
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.js +8 -5
  22. package/dist/index.js.map +1 -1
  23. package/dist/schemas.cjs +3 -3
  24. package/dist/schemas.d.cts +1 -1
  25. package/dist/schemas.d.ts +1 -1
  26. package/dist/schemas.js +2 -2
  27. package/dist/scoring.cjs +3 -3
  28. package/dist/scoring.js +2 -2
  29. package/dist/xapi.cjs +3 -3
  30. package/dist/xapi.js +2 -2
  31. package/package.json +1 -1
  32. package/dist/chunk-OJXTXOUA.js.map +0 -1
  33. package/dist/chunk-RHJ5VNFB.cjs.map +0 -1
  34. /package/dist/{chunk-H7L57ITR.js.map → chunk-2M76F32Y.js.map} +0 -0
  35. /package/dist/{chunk-HTMRJ2SS.js.map → chunk-LNA33IK7.js.map} +0 -0
  36. /package/dist/{chunk-ZBCKQBAB.js.map → chunk-NHGXOOZ2.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @intellectif/lk-core
2
2
 
3
+ ## 0.8.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Close two more fail-open leaks in `redact()`, bind a play budget on an essay slot, and make three guards that could not fail actually fail.
8
+
9
+ Everything here is a defect in 0.8.0 / 7.0.0, found by auditing that release rather than by using it.
10
+
11
+ **`redact()` leaked through two more object leaves.** 0.8.0 fixed `media`, which was classified as a single `public` leaf so the author's object was returned by reference without recursing. The same shape survived in two more places:
12
+
13
+ - **`rubric`.** A grader's `modelAnswer` or `aiModel` parked on the rubric reached the exam client and `assertRedacted` blessed it. The rubric stays learner-visible — it tells a learner what they are graded on — but that now means its _documented_ fields are visible (`label`, and each criterion's `name`, `description`, `weight`), not anything anyone stashes under it.
14
+ - **`feedback` and each blank's `match`.** These are `answer-key`, so the default `reveal: 'none'` drops them entirely and the flaw is invisible there. `reveal: 'after-submit'` keeps them, and kept them **whole**: a grader's private note or tuning knob parked beside the documented fields went to the learner along with the answer key, and the projection aliased the caller's object. An after-submit reveal now shows `correct` / `incorrect` and the documented `TextMatchPolicy` keys, and nothing else.
15
+
16
+ A nested projection that keeps no fields is now omitted rather than emitted as `{}`, so giving an all-`answer-key` object a nested policy does not turn its absence into an empty object. Verified against the published 0.7.1 build: well-formed content redacts byte-identically in **both** reveal modes, so the only behaviour change is that undocumented sidecars stop travelling.
17
+
18
+ **A budgeted recording never bound on a written-response slot.** An essay carrying a recording with `maxPlays` rendered an **unlimited** player — no plays-remaining, nothing counted — which on a listening-and-writing paper is exactly the failure the budget exists to prevent. `<WrittenResponse>` was the one branch of the pager whose props were written out by hand rather than spread, and it had been the branch left behind three separate times: `defaultSubmitted`, the redacted-in-`practice` guard, and the media budget. It now derives its props by subtracting `onComplete` from the same bag the other types receive, so a prop added to that bag cannot be forgotten there again.
19
+
20
+ **And the guard meant to catch that could never fire.** `ActivityMedia`'s "budget declared with no binding" throw sat _after_ the early return for `controls: 'minimal'` — which a budgeted policy always resolves to — so it was dead code for the one case it was written for. Moved ahead of the branch, and given the standalone test that the pager can never provide: `ActivitySequence` throws on a missing `onPlayConsumed` before children mount, and once that callback is supplied it passes a binding, so no pager test reaches this path either way.
21
+
22
+ **Three guards that could not fail.** Found by mutation-testing the suite — breaking each mechanism and checking whether any test noticed:
23
+
24
+ - The last-play confirmation gate had **no coverage at all**, and was silently absorbing the press in two neighbouring resume tests: both used `maxPlays: 2` with one play spent, so the press they made was swallowed by the gate and their assertions held whether playback resumed or a dialog opened. Gutting resume detection left the whole file green. Both now use a budget that clears the gate and assert that playback actually resumed.
25
+ - Partial multiple-choice scoring asserted only that a score with an off-paper option id lands in `[0, 1]` — which holds whether that id is ignored, credited, or treated as wrong. Now pinned to exact values, one of which is only reachable by the correct treatment.
26
+ - The empty-input fuzzy guard was probed with three spaces against a one-character answer, which the length-difference early exit refuses before the guard is ever consulted. A single space now probes the guard itself.
27
+
28
+ Also: `classifyBand`'s float tolerance was pinned only to within `[0, 0.01)` — seven orders of magnitude of slack, all of it in the promoting direction the function exists to prevent — and is now straddled at its real epsilon; and the cross-type resume test gained its `written-response` row, asserting the restored draft survives rather than only that the control is locked.
29
+
3
30
  ## 0.8.0
4
31
 
5
32
  ### Minor Changes
@@ -6,7 +6,7 @@ import {
6
6
  UnknownActivityTypeError,
7
7
  WrittenResponseDataSchema,
8
8
  getActivityTypeDescriptor
9
- } from "./chunk-OJXTXOUA.js";
9
+ } from "./chunk-HCOWSGEZ.js";
10
10
 
11
11
  // src/schemas/item-group.ts
12
12
  import { z } from "zod/v4";
@@ -191,4 +191,4 @@ export {
191
191
  jsonSchemaFor,
192
192
  validateActivity
193
193
  };
194
- //# sourceMappingURL=chunk-H7L57ITR.js.map
194
+ //# sourceMappingURL=chunk-2M76F32Y.js.map
@@ -1,7 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
3
 
4
- var _chunkRHJ5VNFBcjs = require('./chunk-RHJ5VNFB.cjs');
4
+ var _chunkFKH4YT5Xcjs = require('./chunk-FKH4YT5X.cjs');
5
5
 
6
6
  // src/xapi/validators.ts
7
7
  var _v4 = require('zod/v4');
@@ -83,7 +83,7 @@ function validateXAPIStatement(statement) {
83
83
  console.warn("[learning-kit] Invalid xAPI statement (sent anyway):", errors);
84
84
  return;
85
85
  }
86
- throw new (0, _chunkRHJ5VNFBcjs.ActivitySchemaError)("xAPIStatement", errors);
86
+ throw new (0, _chunkFKH4YT5Xcjs.ActivitySchemaError)("xAPIStatement", errors);
87
87
  }
88
88
 
89
89
  // src/xapi/verbs.ts
@@ -246,7 +246,7 @@ var xAPIBuilder = {
246
246
  }
247
247
  };
248
248
  function xapiDefinitionFor(data) {
249
- const descriptor = _chunkRHJ5VNFBcjs.getActivityTypeDescriptor.call(void 0, data.type);
249
+ const descriptor = _chunkFKH4YT5Xcjs.getActivityTypeDescriptor.call(void 0, data.type);
250
250
  const interop = _optionalChain([descriptor, 'optionalAccess', _6 => _6.interop]);
251
251
  if (interop === void 0) {
252
252
  return {};
@@ -266,4 +266,4 @@ function xapiDefinitionFor(data) {
266
266
 
267
267
 
268
268
  exports.validateXAPIStatement = validateXAPIStatement; exports.XAPIVerb = XAPIVerb; exports.XAPI_VERB_DISPLAY = XAPI_VERB_DISPLAY; exports.xAPIBuilder = xAPIBuilder; exports.xapiDefinitionFor = xapiDefinitionFor;
269
- //# sourceMappingURL=chunk-64NBQCRE.cjs.map
269
+ //# sourceMappingURL=chunk-7ACNBDSF.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/learning-kit/learning-kit/packages/lk-core/dist/chunk-64NBQCRE.cjs","../src/xapi/validators.ts","../src/xapi/verbs.ts","../src/xapi/builder.ts"],"names":[],"mappings":"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","file":"/home/runner/work/learning-kit/learning-kit/packages/lk-core/dist/chunk-64NBQCRE.cjs","sourcesContent":[null,"import { z } from 'zod/v4';\nimport { ActivitySchemaError } from '../errors.js';\nimport type { ValidationError } from '../types/activity.js';\nimport type { XAPIStatement } from '../types/xapi.js';\n\nconst UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n// Minimal IRI/URI check: a scheme followed by ':'.\nconst IRI = /^[a-z][a-z0-9+.-]*:/i;\n\nconst langMap = z.record(z.string(), z.string());\nconst extensions = z.record(z.string(), z.unknown());\n\nconst actorSchema = z\n .object({\n objectType: z.literal('Agent'),\n name: z.string().optional(),\n mbox: z\n .string()\n .regex(/^mailto:/, 'mbox must be a mailto IRI')\n .optional(),\n account: z.object({ homePage: z.string(), name: z.string() }).optional(),\n })\n .refine((actor) => actor.mbox !== undefined || actor.account !== undefined, {\n error: 'actor must be identified by an mbox or an account',\n });\n\nconst statementSchema = z.object({\n id: z.string().regex(UUID_V4, 'id must be a UUID v4'),\n actor: actorSchema,\n verb: z.object({\n id: z.string().regex(IRI, 'verb.id must be an IRI'),\n display: langMap,\n }),\n object: z.object({\n objectType: z.literal('Activity'),\n id: z.string().regex(IRI, 'object.id must be an IRI'),\n definition: z\n .object({\n name: langMap.optional(),\n description: langMap.optional(),\n type: z.string().optional(),\n interactionType: z.string().optional(),\n correctResponsesPattern: z.array(z.string()).optional(),\n choices: z.array(z.object({ id: z.string(), description: langMap.optional() })).optional(),\n extensions: extensions.optional(),\n })\n .optional(),\n }),\n result: z\n .object({\n // xAPI 1.0.3 permits scaled in [-1, 1]; the SDK only emits [0, 1].\n score: z\n .object({\n scaled: z.number().min(-1).max(1),\n raw: z.number().optional(),\n min: z.number().optional(),\n max: z.number().optional(),\n })\n .optional(),\n success: z.boolean().optional(),\n completion: z.boolean().optional(),\n duration: z.string().regex(/^P/, 'duration must be an ISO 8601 duration').optional(),\n response: z.string().optional(),\n extensions: extensions.optional(),\n })\n .optional(),\n context: z\n .object({\n platform: z.string().optional(),\n language: z.string().optional(),\n contextActivities: z\n .object({\n parent: z\n .array(z.object({ objectType: z.literal('Activity'), id: z.string() }))\n .optional(),\n grouping: z\n .array(z.object({ objectType: z.literal('Activity'), id: z.string() }))\n .optional(),\n category: z\n .array(z.object({ objectType: z.literal('Activity'), id: z.string() }))\n .optional(),\n other: z\n .array(z.object({ objectType: z.literal('Activity'), id: z.string() }))\n .optional(),\n })\n .optional(),\n extensions: extensions.optional(),\n })\n .optional(),\n timestamp: z\n .string()\n .refine((value) => !Number.isNaN(Date.parse(value)), 'timestamp must be ISO 8601'),\n version: z.literal('1.0.3'),\n});\n\n/** True when running under `NODE_ENV=production`, safe in any environment. */\nfunction isProduction(): boolean {\n const g = globalThis as { process?: { env?: Record<string, string | undefined> } };\n return g.process?.env?.NODE_ENV === 'production';\n}\n\n/**\n * Validates a constructed {@link XAPIStatement} against the xAPI 1.0.3\n * structural contract.\n *\n * In development a structural failure throws (caught early); in production it\n * logs a warning and returns (the statement is still sent — see the design's\n * Error Handling table). Uses a `zod/v4` schema rather than `ajv` + an\n * external JSON Schema to keep `lk-core` dependency-free.\n *\n * NOTE: `ActivitySchemaError` is reused here per the task spec; its\n * `activityType` field carries the sentinel `'xAPIStatement'`. A dedicated\n * `XAPIValidationError` would be semantically cleaner — flagged for review.\n */\nexport function validateXAPIStatement(statement: XAPIStatement): void {\n const result = statementSchema.safeParse(statement);\n if (result.success) {\n return;\n }\n\n const errors: ValidationError[] = result.error.issues.map((issue) => ({\n path: issue.path.map(String),\n message: issue.message,\n code: issue.code,\n }));\n\n if (isProduction()) {\n console.warn('[learning-kit] Invalid xAPI statement (sent anyway):', errors);\n return;\n }\n\n throw new ActivitySchemaError('xAPIStatement', errors);\n}\n","/**\n * The set of xAPI verbs emitted by SDK activities, keyed by a stable\n * identifier mapped to its canonical xAPI verb IRI (xAPI 1.0.3 / ADL registry).\n */\nexport const XAPIVerb = {\n ANSWERED: 'http://adlnet.gov/expapi/verbs/answered',\n COMPLETED: 'http://adlnet.gov/expapi/verbs/completed',\n EXPERIENCED: 'http://adlnet.gov/expapi/verbs/experienced',\n INTERACTED: 'http://adlnet.gov/expapi/verbs/interacted',\n ATTEMPTED: 'http://adlnet.gov/expapi/verbs/attempted',\n PASSED: 'http://adlnet.gov/expapi/verbs/passed',\n FAILED: 'http://adlnet.gov/expapi/verbs/failed',\n WATCHED: 'https://w3id.org/xapi/video/verbs/watched',\n /**\n * Emitted for deferred-grading submissions (e.g. written-response): the\n * learner has submitted work whose grade does not exist yet, so `answered`\n * (which implies a scored response) would be wrong. xAPI 1.0.3 defines no\n * canonical \"submit\" verb; the Activity Streams 1.0 IRI is the established\n * community choice (design decision recorded per Task 25.4).\n */\n SUBMITTED: 'http://activitystrea.ms/schema/1.0/submit',\n /**\n * Emitted when an asynchronous grader produces a grade for work that was\n * previously only SUBMITTED. Distinct from `answered`, which asserts the\n * grade existed at submission time — here the learner acted earlier and the\n * grade arrived later, often from a different actor (an AI or a teacher).\n */\n SCORED: 'http://adlnet.gov/expapi/verbs/scored',\n} as const;\n\n/** Union of the valid `XAPIVerb` keys. */\nexport type XAPIVerbKey = keyof typeof XAPIVerb;\n\n/**\n * Default `en-US` display label for each verb, used to populate\n * `XAPIVerbObject.display` when the builder resolves a verb key.\n */\nexport const XAPI_VERB_DISPLAY: Record<XAPIVerbKey, Record<string, string>> = {\n ANSWERED: { 'en-US': 'answered' },\n COMPLETED: { 'en-US': 'completed' },\n EXPERIENCED: { 'en-US': 'experienced' },\n INTERACTED: { 'en-US': 'interacted' },\n ATTEMPTED: { 'en-US': 'attempted' },\n PASSED: { 'en-US': 'passed' },\n FAILED: { 'en-US': 'failed' },\n WATCHED: { 'en-US': 'watched' },\n SUBMITTED: { 'en-US': 'submitted' },\n SCORED: { 'en-US': 'scored' },\n};\n","import { getActivityTypeDescriptor } from '../registry/index.js';\nimport type { ScoringResult } from '../types/activity.js';\nimport type {\n XAPIActor,\n XAPIContext,\n XAPIObject,\n XAPIResult,\n XAPIStatement,\n} from '../types/xapi.js';\nimport { validateXAPIStatement } from './validators.js';\nimport { XAPI_VERB_DISPLAY, XAPIVerb, type XAPIVerbKey } from './verbs.js';\n\n/** Caller-supplied activity object descriptor (mapped to an xAPI Activity). */\nexport interface XAPIObjectParams {\n /** Activity IRI. */\n id: string;\n name?: Record<string, string>;\n description?: Record<string, string>;\n /** Activity type IRI. */\n type?: string;\n /** xAPI cmi.interaction type (e.g. `choice`, `fill-in`, `long-fill-in`). */\n interactionType?: string;\n /** Correct-responses patterns, per the xAPI CRP format. */\n correctResponsesPattern?: string[];\n /** For choice-family interactions: the available components. */\n choices?: Array<{ id: string; description?: Record<string, string> }>;\n}\n\n/** Parameters for the generic {@link xAPIBuilder.buildStatement}. */\nexport interface XAPIStatementParams {\n actor: XAPIActor;\n verb: XAPIVerbKey;\n object: XAPIObjectParams;\n result?: XAPIResult;\n context?: XAPIContext;\n}\n\n/** Parameters for {@link xAPIBuilder.buildAnsweredStatement} (verb = ANSWERED). */\nexport interface AnsweredStatementParams {\n actor: XAPIActor;\n object: XAPIObjectParams;\n scoringResult: ScoringResult;\n timeSpentMs: number;\n /** Serialized learner response string. */\n response?: string;\n context?: XAPIContext;\n resultExtensions?: Record<string, unknown>;\n}\n\n/**\n * Parameters for {@link xAPIBuilder.buildSubmittedStatement} (verb =\n * SUBMITTED — deferred-grading submissions). Carries NO score, success, or\n * completion: the grade does not exist yet (Req 22.7).\n */\nexport interface SubmittedStatementParams {\n actor: XAPIActor;\n object: XAPIObjectParams;\n timeSpentMs: number;\n /** Serialized learner response string (e.g. the submitted text). */\n response?: string;\n context?: XAPIContext;\n resultExtensions?: Record<string, unknown>;\n}\n\n/** Parameters for {@link xAPIBuilder.buildCompletedStatement} (verb = COMPLETED). */\nexport interface CompletedStatementParams {\n actor: XAPIActor;\n object: XAPIObjectParams;\n scoringResult?: ScoringResult;\n timeSpentMs: number;\n context?: XAPIContext;\n resultExtensions?: Record<string, unknown>;\n}\n\n/**\n * Converts a millisecond duration to an ISO 8601 seconds string with\n * centisecond precision (`PT1.23S`), per the xAPI 1.0.3 recommendation of\n * 0.01-second accuracy. Sub-second durations are preserved (previously any\n * duration under 500 ms collapsed to `PT0S`). Negative inputs (clock skew)\n * clamp to zero — a negative ISO duration is invalid.\n */\nfunction msToIsoDuration(ms: number): string {\n return `PT${(Math.round(Math.max(0, ms) / 10) / 100).toFixed(2)}S`;\n}\n\n/**\n * UUID v4 that works outside secure contexts. `crypto.randomUUID` is\n * SecureContext-gated (undefined on plain-http LAN/staging origins), and a\n * statement is built at SUBMIT time — throwing here would lose the learner's\n * answer. `crypto.getRandomValues` is NOT gated, so the fallback still emits\n * a spec-valid v4 UUID (the statement validator enforces the v4 format).\n */\nfunction uuidv4(): string {\n const c = (\n globalThis as {\n crypto?: { randomUUID?: () => string; getRandomValues?: (a: Uint8Array) => Uint8Array };\n }\n ).crypto;\n if (c?.randomUUID) {\n return c.randomUUID();\n }\n const bytes = new Uint8Array(16);\n if (c?.getRandomValues) {\n c.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = ((bytes[6] as number) & 0x0f) | 0x40;\n bytes[8] = ((bytes[8] as number) & 0x3f) | 0x80;\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/** Builds the xAPI Activity object, including `definition` only when populated. */\nfunction buildObject(params: XAPIObjectParams): XAPIObject {\n const definition: NonNullable<XAPIObject['definition']> = {\n ...(params.name !== undefined ? { name: params.name } : {}),\n ...(params.description !== undefined ? { description: params.description } : {}),\n ...(params.type !== undefined ? { type: params.type } : {}),\n ...(params.interactionType !== undefined ? { interactionType: params.interactionType } : {}),\n ...(params.correctResponsesPattern !== undefined\n ? { correctResponsesPattern: params.correctResponsesPattern }\n : {}),\n ...(params.choices !== undefined ? { choices: params.choices } : {}),\n };\n return {\n objectType: 'Activity',\n id: params.id,\n ...(Object.keys(definition).length > 0 ? { definition } : {}),\n };\n}\n\n/**\n * Maps a {@link ScoringResult} + timing to an {@link XAPIResult} per the\n * normative mapping in the design document.\n */\nfunction buildResult(\n timeSpentMs: number,\n scoringResult: ScoringResult | undefined,\n resultExtensions: Record<string, unknown> | undefined,\n response: string | undefined,\n): XAPIResult {\n return {\n completion: true,\n duration: msToIsoDuration(timeSpentMs),\n ...(scoringResult !== undefined\n ? {\n score: {\n scaled: scoringResult.score,\n raw: scoringResult.score,\n min: 0,\n max: scoringResult.maxScore,\n },\n success: scoringResult.passed,\n }\n : {}),\n ...(response !== undefined ? { response } : {}),\n ...(resultExtensions !== undefined ? { extensions: resultExtensions } : {}),\n };\n}\n\n/**\n * Constructs xAPI 1.0.3 Statements. Every call stamps a fresh UUID v4 `id`,\n * an ISO 8601 `timestamp`, and `version: '1.0.3'`. Not pure by design —\n * statements are unique events.\n */\nexport const xAPIBuilder = {\n buildStatement(params: XAPIStatementParams): XAPIStatement {\n const statement: XAPIStatement = {\n id: uuidv4(),\n actor: params.actor,\n verb: {\n id: XAPIVerb[params.verb],\n display: XAPI_VERB_DISPLAY[params.verb],\n },\n object: buildObject(params.object),\n ...(params.result !== undefined ? { result: params.result } : {}),\n ...(params.context !== undefined ? { context: params.context } : {}),\n timestamp: new Date().toISOString(),\n version: '1.0.3',\n };\n // Design xAPIBuilder step 4: validate every statement (throws in dev,\n // warns in prod). All build*Statement helpers route through here.\n validateXAPIStatement(statement);\n return statement;\n },\n\n buildAnsweredStatement(params: AnsweredStatementParams): XAPIStatement {\n return this.buildStatement({\n actor: params.actor,\n verb: 'ANSWERED',\n object: params.object,\n result: buildResult(\n params.timeSpentMs,\n params.scoringResult,\n params.resultExtensions,\n params.response,\n ),\n ...(params.context !== undefined ? { context: params.context } : {}),\n });\n },\n\n /**\n * Builds a statement for a deferred-grading submission (e.g. a written\n * response). Verb is SUBMITTED and the result deliberately omits `score`,\n * `success`, and `completion` — none of them exist until the asynchronous\n * grader runs; only `duration`, `response`, and extensions are recorded.\n */\n buildSubmittedStatement(params: SubmittedStatementParams): XAPIStatement {\n return this.buildStatement({\n actor: params.actor,\n verb: 'SUBMITTED',\n object: params.object,\n result: {\n duration: msToIsoDuration(params.timeSpentMs),\n ...(params.response !== undefined ? { response: params.response } : {}),\n ...(params.resultExtensions !== undefined ? { extensions: params.resultExtensions } : {}),\n },\n ...(params.context !== undefined ? { context: params.context } : {}),\n });\n },\n\n buildCompletedStatement(params: CompletedStatementParams): XAPIStatement {\n return this.buildStatement({\n actor: params.actor,\n verb: 'COMPLETED',\n object: params.object,\n result: buildResult(\n params.timeSpentMs,\n params.scoringResult,\n params.resultExtensions,\n undefined,\n ),\n ...(params.context !== undefined ? { context: params.context } : {}),\n });\n },\n};\n\n/**\n * Builds the xAPI `object.definition` interop fields for an activity from its\n * REGISTERED descriptor, rather than having every renderer hand-roll them.\n *\n * `ActivityTypeDescriptor.interop` declares the activity-type IRI, the\n * cmi.interaction type and how to derive `correctResponsesPattern`; before\n * this, those fields had no reader anywhere and each component rebuilt the\n * same strings inline — the same \"declared but never used\" defect the SDK\n * fixed elsewhere. Reading them here also means a consumer-registered type\n * gets correct interop for free.\n *\n * Returns an empty object for an unregistered type or one that declares no\n * interop, so it is always safe to spread.\n */\nexport function xapiDefinitionFor(data: { type: string }): Partial<XAPIObjectParams> {\n const descriptor = getActivityTypeDescriptor(data.type);\n const interop = descriptor?.interop;\n if (interop === undefined) {\n return {};\n }\n const pattern = interop.correctResponsesPattern?.(data);\n return {\n ...(interop.xapiActivityTypeIri !== undefined ? { type: interop.xapiActivityTypeIri } : {}),\n ...(interop.xapiInteractionType !== undefined\n ? { interactionType: interop.xapiInteractionType }\n : {}),\n ...(pattern !== undefined && pattern.length > 0 ? { correctResponsesPattern: pattern } : {}),\n };\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/learning-kit/learning-kit/packages/lk-core/dist/chunk-7ACNBDSF.cjs","../src/xapi/validators.ts","../src/xapi/verbs.ts","../src/xapi/builder.ts"],"names":[],"mappings":"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","file":"/home/runner/work/learning-kit/learning-kit/packages/lk-core/dist/chunk-7ACNBDSF.cjs","sourcesContent":[null,"import { z } from 'zod/v4';\nimport { ActivitySchemaError } from '../errors.js';\nimport type { ValidationError } from '../types/activity.js';\nimport type { XAPIStatement } from '../types/xapi.js';\n\nconst UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n// Minimal IRI/URI check: a scheme followed by ':'.\nconst IRI = /^[a-z][a-z0-9+.-]*:/i;\n\nconst langMap = z.record(z.string(), z.string());\nconst extensions = z.record(z.string(), z.unknown());\n\nconst actorSchema = z\n .object({\n objectType: z.literal('Agent'),\n name: z.string().optional(),\n mbox: z\n .string()\n .regex(/^mailto:/, 'mbox must be a mailto IRI')\n .optional(),\n account: z.object({ homePage: z.string(), name: z.string() }).optional(),\n })\n .refine((actor) => actor.mbox !== undefined || actor.account !== undefined, {\n error: 'actor must be identified by an mbox or an account',\n });\n\nconst statementSchema = z.object({\n id: z.string().regex(UUID_V4, 'id must be a UUID v4'),\n actor: actorSchema,\n verb: z.object({\n id: z.string().regex(IRI, 'verb.id must be an IRI'),\n display: langMap,\n }),\n object: z.object({\n objectType: z.literal('Activity'),\n id: z.string().regex(IRI, 'object.id must be an IRI'),\n definition: z\n .object({\n name: langMap.optional(),\n description: langMap.optional(),\n type: z.string().optional(),\n interactionType: z.string().optional(),\n correctResponsesPattern: z.array(z.string()).optional(),\n choices: z.array(z.object({ id: z.string(), description: langMap.optional() })).optional(),\n extensions: extensions.optional(),\n })\n .optional(),\n }),\n result: z\n .object({\n // xAPI 1.0.3 permits scaled in [-1, 1]; the SDK only emits [0, 1].\n score: z\n .object({\n scaled: z.number().min(-1).max(1),\n raw: z.number().optional(),\n min: z.number().optional(),\n max: z.number().optional(),\n })\n .optional(),\n success: z.boolean().optional(),\n completion: z.boolean().optional(),\n duration: z.string().regex(/^P/, 'duration must be an ISO 8601 duration').optional(),\n response: z.string().optional(),\n extensions: extensions.optional(),\n })\n .optional(),\n context: z\n .object({\n platform: z.string().optional(),\n language: z.string().optional(),\n contextActivities: z\n .object({\n parent: z\n .array(z.object({ objectType: z.literal('Activity'), id: z.string() }))\n .optional(),\n grouping: z\n .array(z.object({ objectType: z.literal('Activity'), id: z.string() }))\n .optional(),\n category: z\n .array(z.object({ objectType: z.literal('Activity'), id: z.string() }))\n .optional(),\n other: z\n .array(z.object({ objectType: z.literal('Activity'), id: z.string() }))\n .optional(),\n })\n .optional(),\n extensions: extensions.optional(),\n })\n .optional(),\n timestamp: z\n .string()\n .refine((value) => !Number.isNaN(Date.parse(value)), 'timestamp must be ISO 8601'),\n version: z.literal('1.0.3'),\n});\n\n/** True when running under `NODE_ENV=production`, safe in any environment. */\nfunction isProduction(): boolean {\n const g = globalThis as { process?: { env?: Record<string, string | undefined> } };\n return g.process?.env?.NODE_ENV === 'production';\n}\n\n/**\n * Validates a constructed {@link XAPIStatement} against the xAPI 1.0.3\n * structural contract.\n *\n * In development a structural failure throws (caught early); in production it\n * logs a warning and returns (the statement is still sent — see the design's\n * Error Handling table). Uses a `zod/v4` schema rather than `ajv` + an\n * external JSON Schema to keep `lk-core` dependency-free.\n *\n * NOTE: `ActivitySchemaError` is reused here per the task spec; its\n * `activityType` field carries the sentinel `'xAPIStatement'`. A dedicated\n * `XAPIValidationError` would be semantically cleaner — flagged for review.\n */\nexport function validateXAPIStatement(statement: XAPIStatement): void {\n const result = statementSchema.safeParse(statement);\n if (result.success) {\n return;\n }\n\n const errors: ValidationError[] = result.error.issues.map((issue) => ({\n path: issue.path.map(String),\n message: issue.message,\n code: issue.code,\n }));\n\n if (isProduction()) {\n console.warn('[learning-kit] Invalid xAPI statement (sent anyway):', errors);\n return;\n }\n\n throw new ActivitySchemaError('xAPIStatement', errors);\n}\n","/**\n * The set of xAPI verbs emitted by SDK activities, keyed by a stable\n * identifier mapped to its canonical xAPI verb IRI (xAPI 1.0.3 / ADL registry).\n */\nexport const XAPIVerb = {\n ANSWERED: 'http://adlnet.gov/expapi/verbs/answered',\n COMPLETED: 'http://adlnet.gov/expapi/verbs/completed',\n EXPERIENCED: 'http://adlnet.gov/expapi/verbs/experienced',\n INTERACTED: 'http://adlnet.gov/expapi/verbs/interacted',\n ATTEMPTED: 'http://adlnet.gov/expapi/verbs/attempted',\n PASSED: 'http://adlnet.gov/expapi/verbs/passed',\n FAILED: 'http://adlnet.gov/expapi/verbs/failed',\n WATCHED: 'https://w3id.org/xapi/video/verbs/watched',\n /**\n * Emitted for deferred-grading submissions (e.g. written-response): the\n * learner has submitted work whose grade does not exist yet, so `answered`\n * (which implies a scored response) would be wrong. xAPI 1.0.3 defines no\n * canonical \"submit\" verb; the Activity Streams 1.0 IRI is the established\n * community choice (design decision recorded per Task 25.4).\n */\n SUBMITTED: 'http://activitystrea.ms/schema/1.0/submit',\n /**\n * Emitted when an asynchronous grader produces a grade for work that was\n * previously only SUBMITTED. Distinct from `answered`, which asserts the\n * grade existed at submission time — here the learner acted earlier and the\n * grade arrived later, often from a different actor (an AI or a teacher).\n */\n SCORED: 'http://adlnet.gov/expapi/verbs/scored',\n} as const;\n\n/** Union of the valid `XAPIVerb` keys. */\nexport type XAPIVerbKey = keyof typeof XAPIVerb;\n\n/**\n * Default `en-US` display label for each verb, used to populate\n * `XAPIVerbObject.display` when the builder resolves a verb key.\n */\nexport const XAPI_VERB_DISPLAY: Record<XAPIVerbKey, Record<string, string>> = {\n ANSWERED: { 'en-US': 'answered' },\n COMPLETED: { 'en-US': 'completed' },\n EXPERIENCED: { 'en-US': 'experienced' },\n INTERACTED: { 'en-US': 'interacted' },\n ATTEMPTED: { 'en-US': 'attempted' },\n PASSED: { 'en-US': 'passed' },\n FAILED: { 'en-US': 'failed' },\n WATCHED: { 'en-US': 'watched' },\n SUBMITTED: { 'en-US': 'submitted' },\n SCORED: { 'en-US': 'scored' },\n};\n","import { getActivityTypeDescriptor } from '../registry/index.js';\nimport type { ScoringResult } from '../types/activity.js';\nimport type {\n XAPIActor,\n XAPIContext,\n XAPIObject,\n XAPIResult,\n XAPIStatement,\n} from '../types/xapi.js';\nimport { validateXAPIStatement } from './validators.js';\nimport { XAPI_VERB_DISPLAY, XAPIVerb, type XAPIVerbKey } from './verbs.js';\n\n/** Caller-supplied activity object descriptor (mapped to an xAPI Activity). */\nexport interface XAPIObjectParams {\n /** Activity IRI. */\n id: string;\n name?: Record<string, string>;\n description?: Record<string, string>;\n /** Activity type IRI. */\n type?: string;\n /** xAPI cmi.interaction type (e.g. `choice`, `fill-in`, `long-fill-in`). */\n interactionType?: string;\n /** Correct-responses patterns, per the xAPI CRP format. */\n correctResponsesPattern?: string[];\n /** For choice-family interactions: the available components. */\n choices?: Array<{ id: string; description?: Record<string, string> }>;\n}\n\n/** Parameters for the generic {@link xAPIBuilder.buildStatement}. */\nexport interface XAPIStatementParams {\n actor: XAPIActor;\n verb: XAPIVerbKey;\n object: XAPIObjectParams;\n result?: XAPIResult;\n context?: XAPIContext;\n}\n\n/** Parameters for {@link xAPIBuilder.buildAnsweredStatement} (verb = ANSWERED). */\nexport interface AnsweredStatementParams {\n actor: XAPIActor;\n object: XAPIObjectParams;\n scoringResult: ScoringResult;\n timeSpentMs: number;\n /** Serialized learner response string. */\n response?: string;\n context?: XAPIContext;\n resultExtensions?: Record<string, unknown>;\n}\n\n/**\n * Parameters for {@link xAPIBuilder.buildSubmittedStatement} (verb =\n * SUBMITTED — deferred-grading submissions). Carries NO score, success, or\n * completion: the grade does not exist yet (Req 22.7).\n */\nexport interface SubmittedStatementParams {\n actor: XAPIActor;\n object: XAPIObjectParams;\n timeSpentMs: number;\n /** Serialized learner response string (e.g. the submitted text). */\n response?: string;\n context?: XAPIContext;\n resultExtensions?: Record<string, unknown>;\n}\n\n/** Parameters for {@link xAPIBuilder.buildCompletedStatement} (verb = COMPLETED). */\nexport interface CompletedStatementParams {\n actor: XAPIActor;\n object: XAPIObjectParams;\n scoringResult?: ScoringResult;\n timeSpentMs: number;\n context?: XAPIContext;\n resultExtensions?: Record<string, unknown>;\n}\n\n/**\n * Converts a millisecond duration to an ISO 8601 seconds string with\n * centisecond precision (`PT1.23S`), per the xAPI 1.0.3 recommendation of\n * 0.01-second accuracy. Sub-second durations are preserved (previously any\n * duration under 500 ms collapsed to `PT0S`). Negative inputs (clock skew)\n * clamp to zero — a negative ISO duration is invalid.\n */\nfunction msToIsoDuration(ms: number): string {\n return `PT${(Math.round(Math.max(0, ms) / 10) / 100).toFixed(2)}S`;\n}\n\n/**\n * UUID v4 that works outside secure contexts. `crypto.randomUUID` is\n * SecureContext-gated (undefined on plain-http LAN/staging origins), and a\n * statement is built at SUBMIT time — throwing here would lose the learner's\n * answer. `crypto.getRandomValues` is NOT gated, so the fallback still emits\n * a spec-valid v4 UUID (the statement validator enforces the v4 format).\n */\nfunction uuidv4(): string {\n const c = (\n globalThis as {\n crypto?: { randomUUID?: () => string; getRandomValues?: (a: Uint8Array) => Uint8Array };\n }\n ).crypto;\n if (c?.randomUUID) {\n return c.randomUUID();\n }\n const bytes = new Uint8Array(16);\n if (c?.getRandomValues) {\n c.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = ((bytes[6] as number) & 0x0f) | 0x40;\n bytes[8] = ((bytes[8] as number) & 0x3f) | 0x80;\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/** Builds the xAPI Activity object, including `definition` only when populated. */\nfunction buildObject(params: XAPIObjectParams): XAPIObject {\n const definition: NonNullable<XAPIObject['definition']> = {\n ...(params.name !== undefined ? { name: params.name } : {}),\n ...(params.description !== undefined ? { description: params.description } : {}),\n ...(params.type !== undefined ? { type: params.type } : {}),\n ...(params.interactionType !== undefined ? { interactionType: params.interactionType } : {}),\n ...(params.correctResponsesPattern !== undefined\n ? { correctResponsesPattern: params.correctResponsesPattern }\n : {}),\n ...(params.choices !== undefined ? { choices: params.choices } : {}),\n };\n return {\n objectType: 'Activity',\n id: params.id,\n ...(Object.keys(definition).length > 0 ? { definition } : {}),\n };\n}\n\n/**\n * Maps a {@link ScoringResult} + timing to an {@link XAPIResult} per the\n * normative mapping in the design document.\n */\nfunction buildResult(\n timeSpentMs: number,\n scoringResult: ScoringResult | undefined,\n resultExtensions: Record<string, unknown> | undefined,\n response: string | undefined,\n): XAPIResult {\n return {\n completion: true,\n duration: msToIsoDuration(timeSpentMs),\n ...(scoringResult !== undefined\n ? {\n score: {\n scaled: scoringResult.score,\n raw: scoringResult.score,\n min: 0,\n max: scoringResult.maxScore,\n },\n success: scoringResult.passed,\n }\n : {}),\n ...(response !== undefined ? { response } : {}),\n ...(resultExtensions !== undefined ? { extensions: resultExtensions } : {}),\n };\n}\n\n/**\n * Constructs xAPI 1.0.3 Statements. Every call stamps a fresh UUID v4 `id`,\n * an ISO 8601 `timestamp`, and `version: '1.0.3'`. Not pure by design —\n * statements are unique events.\n */\nexport const xAPIBuilder = {\n buildStatement(params: XAPIStatementParams): XAPIStatement {\n const statement: XAPIStatement = {\n id: uuidv4(),\n actor: params.actor,\n verb: {\n id: XAPIVerb[params.verb],\n display: XAPI_VERB_DISPLAY[params.verb],\n },\n object: buildObject(params.object),\n ...(params.result !== undefined ? { result: params.result } : {}),\n ...(params.context !== undefined ? { context: params.context } : {}),\n timestamp: new Date().toISOString(),\n version: '1.0.3',\n };\n // Design xAPIBuilder step 4: validate every statement (throws in dev,\n // warns in prod). All build*Statement helpers route through here.\n validateXAPIStatement(statement);\n return statement;\n },\n\n buildAnsweredStatement(params: AnsweredStatementParams): XAPIStatement {\n return this.buildStatement({\n actor: params.actor,\n verb: 'ANSWERED',\n object: params.object,\n result: buildResult(\n params.timeSpentMs,\n params.scoringResult,\n params.resultExtensions,\n params.response,\n ),\n ...(params.context !== undefined ? { context: params.context } : {}),\n });\n },\n\n /**\n * Builds a statement for a deferred-grading submission (e.g. a written\n * response). Verb is SUBMITTED and the result deliberately omits `score`,\n * `success`, and `completion` — none of them exist until the asynchronous\n * grader runs; only `duration`, `response`, and extensions are recorded.\n */\n buildSubmittedStatement(params: SubmittedStatementParams): XAPIStatement {\n return this.buildStatement({\n actor: params.actor,\n verb: 'SUBMITTED',\n object: params.object,\n result: {\n duration: msToIsoDuration(params.timeSpentMs),\n ...(params.response !== undefined ? { response: params.response } : {}),\n ...(params.resultExtensions !== undefined ? { extensions: params.resultExtensions } : {}),\n },\n ...(params.context !== undefined ? { context: params.context } : {}),\n });\n },\n\n buildCompletedStatement(params: CompletedStatementParams): XAPIStatement {\n return this.buildStatement({\n actor: params.actor,\n verb: 'COMPLETED',\n object: params.object,\n result: buildResult(\n params.timeSpentMs,\n params.scoringResult,\n params.resultExtensions,\n undefined,\n ),\n ...(params.context !== undefined ? { context: params.context } : {}),\n });\n },\n};\n\n/**\n * Builds the xAPI `object.definition` interop fields for an activity from its\n * REGISTERED descriptor, rather than having every renderer hand-roll them.\n *\n * `ActivityTypeDescriptor.interop` declares the activity-type IRI, the\n * cmi.interaction type and how to derive `correctResponsesPattern`; before\n * this, those fields had no reader anywhere and each component rebuilt the\n * same strings inline — the same \"declared but never used\" defect the SDK\n * fixed elsewhere. Reading them here also means a consumer-registered type\n * gets correct interop for free.\n *\n * Returns an empty object for an unregistered type or one that declares no\n * interop, so it is always safe to spread.\n */\nexport function xapiDefinitionFor(data: { type: string }): Partial<XAPIObjectParams> {\n const descriptor = getActivityTypeDescriptor(data.type);\n const interop = descriptor?.interop;\n if (interop === undefined) {\n return {};\n }\n const pattern = interop.correctResponsesPattern?.(data);\n return {\n ...(interop.xapiActivityTypeIri !== undefined ? { type: interop.xapiActivityTypeIri } : {}),\n ...(interop.xapiInteractionType !== undefined\n ? { interactionType: interop.xapiInteractionType }\n : {}),\n ...(pattern !== undefined && pattern.length > 0 ? { correctResponsesPattern: pattern } : {}),\n };\n}\n"]}
@@ -261,6 +261,15 @@ var WrittenResponseRubricSchema = _v4.z.looseObject({
261
261
  label: _v4.z.string().optional(),
262
262
  criteria: _v4.z.array(WrittenResponseRubricCriterionSchema).min(1)
263
263
  });
264
+ var RedactedWrittenResponseRubricCriterionSchema = _v4.z.strictObject({
265
+ name: _v4.z.string().min(1),
266
+ description: _v4.z.string().optional(),
267
+ weight: _v4.z.number().min(0)
268
+ });
269
+ var RedactedWrittenResponseRubricSchema = _v4.z.strictObject({
270
+ label: _v4.z.string().optional(),
271
+ criteria: _v4.z.array(RedactedWrittenResponseRubricCriterionSchema).min(1)
272
+ });
264
273
  var WrittenResponseDataSchema = _v4.z.looseObject({
265
274
  schemaVersion: _v4.z.literal("1.0"),
266
275
  type: _v4.z.literal("written-response"),
@@ -330,7 +339,7 @@ var RedactedWrittenResponseDataSchema = _v4.z.strictObject({
330
339
  promptHtml: _v4.z.string().optional(),
331
340
  minWords: _v4.z.number().int().min(0),
332
341
  maxWords: _v4.z.number().int().min(1),
333
- rubric: WrittenResponseRubricSchema.optional(),
342
+ rubric: RedactedWrittenResponseRubricSchema.optional(),
334
343
  languageTarget: _v4.z.string().optional()
335
344
  });
336
345
 
@@ -564,6 +573,20 @@ var SHARED_PUBLIC_FIELDS = {
564
573
  learningObjectives: "public",
565
574
  difficultyLevel: "public"
566
575
  };
576
+ var FEEDBACK_FIELD_POLICY = {
577
+ correct: "answer-key",
578
+ incorrect: "answer-key"
579
+ };
580
+ var TEXT_MATCH_FIELD_POLICY = {
581
+ caseSensitive: "answer-key",
582
+ trim: "answer-key",
583
+ normalize: "answer-key",
584
+ foldDiacritics: "answer-key",
585
+ collapseInnerWhitespace: "answer-key",
586
+ ignorePunctuation: "answer-key",
587
+ levenshtein: "answer-key",
588
+ locale: "answer-key"
589
+ };
567
590
  var MULTIPLE_CHOICE_FIELD_POLICY = {
568
591
  ...SHARED_PUBLIC_FIELDS,
569
592
  question: "public",
@@ -571,7 +594,7 @@ var MULTIPLE_CHOICE_FIELD_POLICY = {
571
594
  mode: "public",
572
595
  shuffle: "public",
573
596
  scoringStrategy: "answer-key",
574
- feedback: "answer-key",
597
+ feedback: FEEDBACK_FIELD_POLICY,
575
598
  options: {
576
599
  id: "public",
577
600
  text: "public",
@@ -584,17 +607,26 @@ var FILL_IN_THE_BLANKS_FIELD_POLICY = {
584
607
  passage: "public",
585
608
  passageHtml: "public",
586
609
  scoringStrategy: "answer-key",
587
- feedback: "answer-key",
610
+ feedback: FEEDBACK_FIELD_POLICY,
588
611
  blanks: {
589
612
  id: "public",
590
613
  hint: "public",
591
614
  acceptedAnswers: "answer-key",
592
615
  caseSensitive: "answer-key",
593
616
  trimWhitespace: "answer-key",
594
- match: "answer-key",
617
+ match: TEXT_MATCH_FIELD_POLICY,
595
618
  feedback: "answer-key"
596
619
  }
597
620
  };
621
+ var RUBRIC_FIELD_POLICY = {
622
+ label: "public",
623
+ // Applies to every element of the array.
624
+ criteria: {
625
+ name: "public",
626
+ description: "public",
627
+ weight: "public"
628
+ }
629
+ };
598
630
  var WRITTEN_RESPONSE_FIELD_POLICY = {
599
631
  ...SHARED_PUBLIC_FIELDS,
600
632
  prompt: "public",
@@ -602,13 +634,15 @@ var WRITTEN_RESPONSE_FIELD_POLICY = {
602
634
  minWords: "public",
603
635
  maxWords: "public",
604
636
  languageTarget: "public",
605
- feedback: "answer-key",
637
+ feedback: FEEDBACK_FIELD_POLICY,
606
638
  // A rubric is a LEARNER affordance, not a grader secret: it tells the
607
639
  // learner what they are being graded on, which is pedagogically the point
608
640
  // of publishing one. (Classifying it author-only broke real deployments
609
641
  // that render a rubric panel during the attempt.) A deployment that wants
610
642
  // it hidden can tighten this per call via `redact(data, { policy })`.
611
- rubric: "public"
643
+ // Classified field by field so that stays true of the rubric's DOCUMENTED
644
+ // fields only — see {@link RUBRIC_FIELD_POLICY}.
645
+ rubric: RUBRIC_FIELD_POLICY
612
646
  };
613
647
  var multipleChoiceType = defineActivityType({
614
648
  type: "multiple-choice",
@@ -711,4 +745,4 @@ registerActivityType(writtenResponseType);
711
745
 
712
746
 
713
747
  exports.countWords = countWords; exports.ActivitySchemaError = ActivitySchemaError; exports.UnknownActivityTypeError = UnknownActivityTypeError; exports.RedactedScoringError = RedactedScoringError; exports.DeferredScoringError = DeferredScoringError; exports.FeedbackSchema = FeedbackSchema; exports.MediaUrlSchema = MediaUrlSchema; exports.NativeControlHintSchema = NativeControlHintSchema; exports.MediaPlaybackSchema = MediaPlaybackSchema; exports.MediaSchema = MediaSchema; exports.RedactedMediaSchema = RedactedMediaSchema; 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.MEDIA_FIELD_POLICY = MEDIA_FIELD_POLICY; exports.multipleChoiceType = multipleChoiceType; exports.fillInTheBlanksType = fillInTheBlanksType; exports.writtenResponseType = writtenResponseType;
714
- //# sourceMappingURL=chunk-RHJ5VNFB.cjs.map
748
+ //# sourceMappingURL=chunk-FKH4YT5X.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/learning-kit/learning-kit/packages/lk-core/dist/chunk-FKH4YT5X.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-FKH4YT5X.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\n/**\n * Hints to the BROWSER'S OWN control bar, emitted as `controlsList` tokens.\n *\n * They remove a button, never a capability, and only in engines that implement\n * `controlsList` at all. `hide-download` does NOT prevent a download — the URL\n * is in the page and the bytes are in the network panel. If a recording must\n * not be kept, issue a short-lived signed URL; that is the consuming\n * application's control, not the SDK's.\n *\n * Meaningful only alongside the native bar: there is nothing to hint at once\n * the SDK owns the transport.\n */\nexport const NativeControlHintSchema = z.enum(['hide-download', 'hide-rate']);\n\n/**\n * How an audio recording may be played.\n *\n * STRICT on purpose: an unknown key here is an authoring error. {@link\n * MediaSchema} is loose, so without this a typo one level down (`maxPlay`,\n * `seeking`) would be silently accepted and the whole policy would quietly do\n * nothing on an exam that believed it was enforced.\n *\n * Nothing here is required together with anything else — `{ maxPlays: 2 }` is a\n * complete, valid policy, because `controls` and `seek` RESOLVE to the only\n * values that can keep that promise. The refinements below reject only a\n * combination an author wrote **explicitly** that the renderer cannot honour.\n */\nexport const MediaPlaybackSchema = z.strictObject({\n /**\n * `native` (the resolved default) renders today's `<audio controls>`\n * unchanged. `minimal` — resolved automatically whenever any enforcement\n * field is set — replaces the browser bar with the SDK transport:\n * play/pause, elapsed/total, mute, volume, optional speed, optional\n * scrubber, and a live plays-remaining status.\n */\n controls: z.enum(['native', 'minimal']).optional(),\n /**\n * How many times the recording may be STARTED. A play is consumed when\n * playback begins from anywhere other than where it last stopped, so\n * pausing, resuming, and paging between the questions of one listening\n * group are all free. Enforced in `practice` and `exam`; never in `review`.\n */\n maxPlays: z.number().int().min(1).max(20).optional(),\n /** `none` renders no scrubber and reverts an out-of-band seek to the high-water mark. */\n seek: z.enum(['allow', 'none']).optional(),\n /** `fixed` renders no speed control and snaps `playbackRate` back to 1. */\n rate: z.enum(['allow', 'fixed']).optional(),\n /** Advisory only. See {@link NativeControlHintSchema}. */\n nativeControlHints: z.array(NativeControlHintSchema).min(1).max(2).optional(),\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 playback: MediaPlaybackSchema.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 .refine((m) => m.playback === undefined || m.type === 'audio', {\n error:\n 'playback policy is supported on audio media only. An embed is a provider iframe the SDK cannot control at all (it has no reliable JS API for a third-party player); an image has nothing to play; video is deliberately deferred, because a video transport must also own fullscreen and Picture-in-Picture and the SDK will not pretend to govern those yet.',\n path: ['playback'],\n })\n .refine(\n (m) =>\n m.playback?.controls !== 'native' ||\n (m.playback.maxPlays === undefined &&\n m.playback.seek !== 'none' &&\n m.playback.rate !== 'fixed'),\n {\n error:\n 'controls: \"native\" cannot carry maxPlays, seek: \"none\" or rate: \"fixed\". The browser\\'s own bar keeps its play button enabled after a budget is spent, and keeps a scrubber that would move and then silently snap back — a control that looks operable and does nothing (WCAG 3.2.2 / 4.1.3). Omit `controls` and the SDK transport is used automatically, or use nativeControlHints for a cosmetic hint.',\n path: ['playback', 'controls'],\n },\n )\n .refine((m) => m.playback?.maxPlays === undefined || m.playback.seek !== 'allow', {\n error:\n 'maxPlays cannot be combined with seek: \"allow\". A play is consumed when playback starts from somewhere other than where it stopped, so scrubbing back mid-play would replay the whole recording without spending anything. Omit `seek` — it resolves to \"none\" under a budget.',\n path: ['playback', 'seek'],\n })\n .refine(\n (m) =>\n m.playback?.controls !== 'minimal' ||\n m.playback.maxPlays !== undefined ||\n m.playback.seek === 'none' ||\n m.playback.rate === 'fixed',\n {\n error:\n 'controls: \"minimal\" with nothing to enforce trades the browser\\'s localized, familiar control bar for the SDK\\'s, and buys nothing. Set maxPlays, seek: \"none\" or rate: \"fixed\", or omit `controls`.',\n path: ['playback', 'controls'],\n },\n )\n .refine(\n (m) =>\n m.playback?.nativeControlHints === undefined ||\n (m.playback.controls !== 'minimal' &&\n m.playback.maxPlays === undefined &&\n m.playback.seek !== 'none' &&\n m.playback.rate !== 'fixed'),\n {\n error:\n \"nativeControlHints only affects the browser's own control bar, and an enforcing policy replaces that bar with the SDK transport — so the hints would be silently inert. Use them on an otherwise unrestricted recording, or drop them.\",\n path: ['playback', 'nativeControlHints'],\n },\n );\n\n/**\n * The strict counterpart of {@link MediaSchema}, for the redacted shapes.\n *\n * `RedactedActivityDataSchema` and `RedactedStimulusSchema` are `strictObject`\n * precisely so an unknown key is a validation failure rather than a\n * passthrough — but they embedded the LOOSE `MediaSchema`, so the strictness\n * stopped at the media boundary and `media.secretAnswerHint` passed\n * `assertRedacted`. This closes it.\n */\nexport const RedactedMediaSchema = z\n .strictObject({\n type: z.enum(['image', 'audio', 'video', 'embed']),\n url: MediaUrlSchema,\n alt: z.string().min(1).optional(),\n captionsUrl: MediaUrlSchema.optional(),\n playback: MediaPlaybackSchema.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 * The strict counterparts of the rubric schemas, for the redacted shapes.\n *\n * The authoring schemas above are loose on purpose (Req 22.5: a consumer's\n * rubric sidecars must survive `validateActivity` verbatim). The REDACTED\n * shape must not be: `RedactedWrittenResponseDataSchema` is a `strictObject`\n * precisely so an unknown key is a validation failure, and embedding the loose\n * rubric there meant the strictness stopped at the rubric boundary — a\n * `rubric.modelAnswer` reached the learner and `assertRedacted` blessed it.\n */\nexport const RedactedWrittenResponseRubricCriterionSchema = z.strictObject({\n name: z.string().min(1),\n description: z.string().optional(),\n weight: z.number().min(0),\n});\n\nexport const RedactedWrittenResponseRubricSchema = z.strictObject({\n label: z.string().optional(),\n criteria: z.array(RedactedWrittenResponseRubricCriterionSchema).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 { RedactedMediaSchema } from './media.js';\nimport { RedactedWrittenResponseRubricSchema } 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 /** Slot identity, carried through redaction so the client and the plan agree. */\n slotKey: z.string().min(1).optional(),\n schemaVersion: z.literal('1.0'),\n id: z.string().min(1),\n title: z.string().min(1),\n media: RedactedMediaSchema.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: RedactedWrittenResponseRubricSchema.optional(),\n languageTarget: z.string().optional(),\n});\n\n/**\n * The learner-safe SHAPE of each built-in type, derived from the strict schema\n * above rather than hand-written beside it.\n *\n * `redact()` returns {@link RedactedActivityData}, which proves a payload is\n * learner-safe but is index-signature typed — it deliberately says nothing\n * about what the payload still CONTAINS. That is right for the assertion and\n * useless for anything that has to render or transport the result, so every\n * integrator ends up re-declaring these interfaces by hand and they drift the\n * moment a schema changes. Deriving them with `z.infer` means the type and the\n * validator can never disagree.\n *\n * Use them for the payload a server sends an exam client, and for the props of\n * a renderer that must never see an answer key.\n */\nexport type RedactedMultipleChoiceOption = z.infer<typeof RedactedMultipleChoiceOptionSchema>;\n/** A Multiple Choice item with the answer key, feedback and strategy removed. */\nexport type RedactedMultipleChoiceData = z.infer<typeof RedactedMultipleChoiceDataSchema>;\n/** A blank with its accepted answers and matching rules removed; the hint survives. */\nexport type RedactedBlankConfig = z.infer<typeof RedactedBlankConfigSchema>;\n/** A Fill-in-the-Blanks item with every accepted answer removed. */\nexport type RedactedFillInTheBlanksData = z.infer<typeof RedactedFillInTheBlanksDataSchema>;\n/** A Written Response item; the rubric survives, because it tells the learner what is assessed. */\nexport type RedactedWrittenResponseData = z.infer<typeof RedactedWrittenResponseDataSchema>;\n\n/**\n * Discriminated union of every built-in redacted activity. Narrow it on\n * `type`, exactly as you would {@link ActivityData}:\n *\n * ```ts\n * function render(item: RedactedActivity) {\n * if (item.type === 'multiple-choice') {\n * return item.options.map((option) => option.text); // no `isCorrect` to leak\n * }\n * }\n * ```\n */\nexport type RedactedActivity =\n | RedactedMultipleChoiceData\n | RedactedFillInTheBlanksData\n | RedactedWrittenResponseData;\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 // A learner who typed nothing made no typo. Levenshtein distance from an\n // empty string is just the answer's length, so `levenshtein: 1` on a\n // one-letter blank (\"a\", \"I\") marked an UNANSWERED blank correct — the\n // fuzzy stage rescuing a blank rather than a misspelling. No author enabling\n // typo tolerance intends that, so an empty input never reaches this stage.\n // Exact and normalized matching are untouched: an author who genuinely lists\n // \"\" as an accepted answer still gets it, deliberately, one stage earlier.\n const maxDistance = policy.levenshtein ?? 0;\n if (\n maxDistance > 0 &&\n foldedInput.trim().length > 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 PUBLIC: a rubric tells the learner what they are assessed on,\n * and a deployment that wants it hidden tightens it per call — see the\n * `rubric` entry in the written-response policy below.\n */\n/**\n * `media` classified field by field, not as one opaque leaf.\n *\n * A scalar `Sensitivity` classifies the WHOLE field, so `media: 'public'`\n * returned the author's object by reference without recursing — and an\n * unclassified key nested under it (`media.secretAnswerHint`) survived\n * `redact()` AND passed `assertRedacted()`. The fail-closed contract the SDK\n * documents (\"a field the policy does not classify is removed\") stopped at the\n * media boundary. It no longer does.\n *\n * Everything here is public by necessity: the client is the thing that renders\n * and enforces the policy, and \"1 play remaining\" is text the learner has to\n * read. None of it is an answer key — a listening paper's answer key is\n * `stimulus.transcript`, which stays `author-only`.\n */\nexport const MEDIA_FIELD_POLICY: FieldPolicy = {\n type: 'public',\n url: 'public',\n alt: 'public',\n captionsUrl: 'public',\n playback: {\n controls: 'public',\n maxPlays: 'public',\n seek: 'public',\n rate: 'public',\n nativeControlHints: 'public',\n },\n};\n\nconst SHARED_PUBLIC_FIELDS: FieldPolicy = {\n schemaVersion: 'public',\n type: 'public',\n id: 'public',\n // Assembly metadata, not content: it names the slot this item occupies in a\n // paper. It has to survive redaction, or the exam client derives positional\n // slot ids while the server's stored plan holds keyed ones, and the\n // responses cannot be matched back to the attempt.\n slotKey: 'public',\n title: 'public',\n media: MEDIA_FIELD_POLICY,\n passThreshold: 'public',\n locale: 'public',\n learningObjectives: 'public',\n difficultyLevel: 'public',\n};\n\n/**\n * Activity-level feedback, classified field by field.\n *\n * The third object leaf to need this, after `media` and `rubric`, and the same\n * defect each time: a scalar classification assigns the author's object by\n * reference without recursing, so under `reveal: 'after-submit'` an\n * unclassified key nested inside it — a grader note, an internal cost — was\n * forwarded to the learner along with the answer key, and `redact()` handed\n * back an alias of the caller's object. `FeedbackSchema` is loose, so anything\n * can be parked there.\n *\n * Both fields stay `answer-key`: activity feedback IS the answer key's\n * commentary, so `reveal: 'none'` still drops the whole object.\n */\nexport const FEEDBACK_FIELD_POLICY: FieldPolicy = {\n correct: 'answer-key',\n incorrect: 'answer-key',\n};\n\n/**\n * A blank's matching tolerances, classified key by key.\n *\n * Same reasoning as {@link FEEDBACK_FIELD_POLICY}: `TextMatchPolicy` is a\n * documented set of knobs, and a consumer's private tuning parked beside them\n * is not part of what an after-submit reveal promises to show.\n */\nexport const TEXT_MATCH_FIELD_POLICY: FieldPolicy = {\n caseSensitive: 'answer-key',\n trim: 'answer-key',\n normalize: 'answer-key',\n foldDiacritics: 'answer-key',\n collapseInnerWhitespace: 'answer-key',\n ignorePunctuation: 'answer-key',\n levenshtein: 'answer-key',\n locale: 'answer-key',\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: FEEDBACK_FIELD_POLICY,\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: FEEDBACK_FIELD_POLICY,\n blanks: {\n id: 'public',\n hint: 'public',\n acceptedAnswers: 'answer-key',\n caseSensitive: 'answer-key',\n trimWhitespace: 'answer-key',\n match: TEXT_MATCH_FIELD_POLICY,\n feedback: 'answer-key',\n },\n};\n\n/**\n * The rubric classified field by field, not as one opaque leaf.\n *\n * A scalar `Sensitivity` classifies the WHOLE field, so `rubric: 'public'`\n * returned the author's object by reference without recursing — and an\n * unclassified key nested under it survived `redact()` AND passed\n * `assertRedacted()`. A grader's `modelAnswer` or `aiModel` stashed on the\n * rubric went straight to the exam client. Same defect as the one `media` had,\n * one field along: \"the rubric is public\" has to mean its documented fields\n * are public, not that anything anyone parks under it is.\n */\nexport const RUBRIC_FIELD_POLICY: FieldPolicy = {\n label: 'public',\n // Applies to every element of the array.\n criteria: {\n name: 'public',\n description: 'public',\n weight: 'public',\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: FEEDBACK_FIELD_POLICY,\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 // Classified field by field so that stays true of the rubric's DOCUMENTED\n // fields only — see {@link RUBRIC_FIELD_POLICY}.\n rubric: RUBRIC_FIELD_POLICY,\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"]}