@intellectif/lk-core 0.22.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +5 -5
- package/dist/{activity-CYc7SD3d.d.cts → activity-DgpBJeBc.d.cts} +11 -15
- package/dist/{activity-CYc7SD3d.d.ts → activity-DgpBJeBc.d.ts} +11 -15
- package/dist/ai-check.cjs +8 -8
- package/dist/ai-check.d.cts +1 -1
- package/dist/ai-check.d.ts +1 -1
- package/dist/ai-check.js +3 -3
- package/dist/{chunk-2CV6NU32.cjs → chunk-2JT2WUQ2.cjs} +77 -39
- package/dist/chunk-2JT2WUQ2.cjs.map +1 -0
- package/dist/{chunk-D4TOPSHV.cjs → chunk-DSDXL3JO.cjs} +1216 -1105
- package/dist/chunk-DSDXL3JO.cjs.map +1 -0
- package/dist/{chunk-D3GP5MET.js → chunk-FBKTION6.js} +1093 -982
- package/dist/chunk-FBKTION6.js.map +1 -0
- package/dist/{chunk-Q7MV3IXJ.js → chunk-IARLJ5MW.js} +15 -16
- package/dist/chunk-IARLJ5MW.js.map +1 -0
- package/dist/{chunk-5UCR4UNR.js → chunk-RCZCP5OE.js} +12 -7
- package/dist/chunk-RCZCP5OE.js.map +1 -0
- package/dist/{chunk-X5DAXBJI.js → chunk-SJVRUPJT.js} +2 -2
- package/dist/{chunk-EOFWSGLU.cjs → chunk-TJJFKKH2.cjs} +4 -4
- package/dist/{chunk-EOFWSGLU.cjs.map → chunk-TJJFKKH2.cjs.map} +1 -1
- package/dist/{chunk-WQVZPIN7.cjs → chunk-VSH7ECQL.cjs} +44 -45
- package/dist/chunk-VSH7ECQL.cjs.map +1 -0
- package/dist/{chunk-OPMY2MNS.cjs → chunk-W5KB3DNT.cjs} +19 -14
- package/dist/chunk-W5KB3DNT.cjs.map +1 -0
- package/dist/{chunk-Z7QQZ53L.js → chunk-XS6U2ARZ.js} +70 -32
- package/dist/chunk-XS6U2ARZ.js.map +1 -0
- package/dist/index-BVNeSCWC.d.ts +548 -0
- package/dist/index-BlOLlwOi.d.cts +548 -0
- package/dist/index.cjs +129 -206
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +115 -15
- package/dist/index.d.ts +115 -15
- package/dist/index.js +26 -103
- package/dist/index.js.map +1 -1
- package/dist/schemas.cjs +3 -94
- package/dist/schemas.cjs.map +1 -1
- package/dist/schemas.d.cts +2 -3
- package/dist/schemas.d.ts +2 -3
- package/dist/schemas.js +6 -97
- package/dist/scoring.cjs +3 -3
- package/dist/scoring.d.cts +2 -2
- package/dist/scoring.d.ts +2 -2
- package/dist/scoring.js +2 -2
- package/dist/xapi.cjs +3 -3
- package/dist/xapi.d.cts +1 -1
- package/dist/xapi.d.ts +1 -1
- package/dist/xapi.js +2 -2
- package/package.json +4 -4
- package/vectors/README.md +32 -2
- package/vectors/scoring.json +1 -498
- package/vectors/validation.json +6742 -0
- package/vectors/validation.mjs +195 -0
- package/dist/chunk-2CV6NU32.cjs.map +0 -1
- package/dist/chunk-5UCR4UNR.js.map +0 -1
- package/dist/chunk-D3GP5MET.js.map +0 -1
- package/dist/chunk-D4TOPSHV.cjs.map +0 -1
- package/dist/chunk-OPMY2MNS.cjs.map +0 -1
- package/dist/chunk-Q7MV3IXJ.js.map +0 -1
- package/dist/chunk-WQVZPIN7.cjs.map +0 -1
- package/dist/chunk-Z7QQZ53L.js.map +0 -1
- package/dist/index-0Z3-nnAP.d.ts +0 -2303
- package/dist/index-CxxiNSSH.d.cts +0 -2303
- /package/dist/{chunk-X5DAXBJI.js.map → chunk-SJVRUPJT.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ai-text.ts","../src/ai.ts","../src/ai-writing.ts"],"sourcesContent":["/**\n * How the SDK reads text a model wrote, before a learner does. Internal: shared\n * by the AI checks, and exported by none of the package's entry points.\n */\n\n/**\n * Plain text as the learner will read it: line endings unified, control\n * characters other than line breaks and tabs removed, runs of blank lines\n * collapsed, the ends trimmed.\n */\nexport function cleanText(text: string): string {\n return (\n text\n .replace(/\\r\\n?/g, '\\n')\n // biome-ignore lint/suspicious/noControlCharactersInRegex: removing them is the point\n .replace(/[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u009F\\u2028\\u2029]/g, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n );\n}\n","import { cleanText } from './ai-text.js';\nimport { PLACEHOLDER_RE } from './schemas/fill-in-the-blanks.js';\nimport { alignDictation } from './scoring/dictation/align.js';\nimport { isGradeInRange } from './scoring/grade-numbers.js';\nimport { score } from './scoring/index.js';\nimport type {\n ActivityData,\n DictationData,\n FillInTheBlanksData,\n GapSelectData,\n ItemOutcome,\n LearnerResponse,\n MultipleChoiceData,\n ScoringDetail,\n} from './types/activity.js';\nimport type {\n AiActivityInput,\n AiExplanationRequest,\n AiFeature,\n AiGrade,\n AiHintRequest,\n AiItemFacts,\n AiProvenance,\n AiRefusal,\n AiSupportedActivityType,\n AiTextResult,\n AiVerdict,\n AiWordFact,\n} from './types/ai.js';\nimport type { GraderUsage } from './types/grading.js';\n\n/**\n * The activity types the SDK builds facts for, and which of them take hints.\n * A dictation takes none: its answer is the whole sentence, and it has\n * word-by-word hints of its own.\n */\nexport const AI_EXPLANATION_TYPES: readonly AiSupportedActivityType[] = [\n 'multiple-choice',\n 'fill-in-the-blanks',\n 'gap-select',\n 'dictation',\n];\nexport const AI_HINT_TYPES: readonly AiSupportedActivityType[] = [\n 'multiple-choice',\n 'fill-in-the-blanks',\n 'gap-select',\n];\n\n/** The longest AI text shown, in code points. Longer is refused, not cut. */\nexport const AI_TEXT_MAX_LENGTH = 2000;\n\n/**\n * Whether the SDK supports `feature` for this activity type: explanations and\n * hints for the types it builds facts for, and writing feedback for a written\n * response. A read-aloud, or a consumer's own type, has none.\n */\nexport function aiSupports(activityType: string, feature: AiFeature): boolean {\n if (feature === 'writing-feedback') {\n return activityType === 'written-response';\n }\n const types = feature === 'hint' ? AI_HINT_TYPES : AI_EXPLANATION_TYPES;\n return (types as readonly string[]).includes(activityType);\n}\n\n/**\n * Whether the item's author allows `feature`. Only an explicit `false`\n * switches it off: an author can refuse a feature, never force one on.\n *\n * Writing feedback reads `explanations`: both are a model's words about the\n * learner's own answer, and an author who switched one off has switched off\n * the other.\n */\nexport function aiAllowedByContent(data: AiActivityInput, feature: AiFeature): boolean {\n const permissions = (data as { ai?: unknown }).ai;\n if (typeof permissions !== 'object' || permissions === null) {\n return true;\n }\n const key = feature === 'hint' ? 'hints' : 'explanations';\n return (permissions as Record<string, unknown>)[key] !== false;\n}\n\n/** A grade as an explanation must speak to it: all marks, some, or none. */\nexport function aiGradeOf(result: { score: number; maxScore: number; passed: boolean }): AiGrade {\n const ratio = result.maxScore > 0 ? result.score / result.maxScore : 0;\n let category: AiVerdict = 'partly-correct';\n if (ratio >= 1 - 1e-9) {\n category = 'correct';\n } else if (ratio <= 1e-9) {\n category = 'incorrect';\n }\n return { score: result.score, maxScore: result.maxScore, passed: result.passed, category };\n}\n\n// ── Facts ──────────────────────────────────────────────────────────────\n\n/**\n * The ids of a passage's placeholders, in the order they appear. Read with the\n * schema's own expression, so a passage means here what it means to validation.\n */\nfunction placeholderOrder(passage: string): string[] {\n return [...passage.matchAll(PLACEHOLDER_RE)].map((match) => match[1] as string);\n}\n\n/** The passage with each placeholder written as `[n]`, in passage order. */\nfunction numberedPassage(passage: string): string {\n let position = 0;\n return passage.replace(PLACEHOLDER_RE, () => {\n position += 1;\n return `[${position}]`;\n });\n}\n\n/**\n * The `correct` flag a detail stored before 0.3 carries instead of an\n * `outcome`. On a blank, a gap or a word it meant the part was right; on a\n * multiple-choice option, that the learner acted rightly on it.\n */\nconst legacyCorrect = (detail: ScoringDetail): boolean =>\n (detail as { correct?: unknown }).correct === true;\n\n/** Whether a blank, a gap or a word was right, read from a server's detail. */\nfunction detailRight(detail: ScoringDetail): boolean {\n const outcome: unknown = detail.outcome;\n return outcome === undefined ? legacyCorrect(detail) : outcome === 'correct';\n}\n\n/** Whether an option is part of the answer, read from a server's detail (as the renderer reads it). */\nfunction isAnswerOption(detail: ScoringDetail, chosen: boolean): boolean {\n switch (detail.outcome) {\n case 'correct':\n case 'incorrect-omission':\n return true;\n case 'incorrect':\n case 'correct-omission':\n return false;\n default:\n return chosen ? legacyCorrect(detail) : !legacyCorrect(detail);\n }\n}\n\nconst firstString = (value: string | string[] | undefined): string | undefined =>\n Array.isArray(value) ? value[0] : value;\n\nconst listOf = (value: string | string[] | undefined): string[] | null =>\n value === undefined ? null : Array.isArray(value) ? [...value] : [value];\n\n/** The response a learner has before touching anything. */\nfunction emptyResponse(type: string): LearnerResponse | null {\n switch (type) {\n case 'multiple-choice':\n return { type: 'multiple-choice', selectedOptionIds: [] };\n case 'fill-in-the-blanks':\n return { type: 'fill-in-the-blanks', answers: {} };\n case 'gap-select':\n return { type: 'gap-select', selections: {} };\n case 'dictation':\n return { type: 'dictation', text: '' };\n default:\n return null;\n }\n}\n\nconst base = (data: AiActivityInput) => ({\n activityId: data.id,\n title: data.title,\n ...(typeof data.locale === 'string' ? { locale: data.locale } : {}),\n});\n\n/**\n * The facts a model is given about one item and one answer. `details` are the\n * SDK's per-part verdicts — from its own scorer, or from a server's outcome in\n * review — and every `correct` is `null` without them. A redacted item yields\n * facts without a key, which is what the learner's side holds; a port that\n * needs the key loads the item on its server.\n *\n * `null` for a type the SDK builds no facts for.\n */\nexport function buildAiFacts(\n data: AiActivityInput,\n response: LearnerResponse | null,\n details: readonly ScoringDetail[] | null,\n): AiItemFacts | null {\n const byId = new Map((details ?? []).map((detail) => [detail.itemId, detail]));\n const full = (data as { redacted?: unknown }).redacted !== true;\n switch (data.type) {\n case 'multiple-choice': {\n const item = data as unknown as MultipleChoiceData;\n const chosen = new Set(\n response?.type === 'multiple-choice' ? response.selectedOptionIds : [],\n );\n return {\n ...base(data),\n activityType: 'multiple-choice',\n question: item.question,\n mode: item.mode,\n options: item.options.map((option) => {\n const detail = byId.get(option.id);\n const wasChosen = chosen.has(option.id);\n let correct: boolean | null = null;\n if (full && typeof option.isCorrect === 'boolean') {\n correct = option.isCorrect;\n } else if (detail !== undefined) {\n correct = isAnswerOption(detail, wasChosen);\n }\n return {\n id: option.id,\n text: option.text,\n chosen: wasChosen,\n correct,\n ...(typeof option.feedback === 'string' ? { feedback: option.feedback } : {}),\n };\n }),\n };\n }\n case 'fill-in-the-blanks': {\n const item = data as unknown as FillInTheBlanksData;\n const answers = response?.type === 'fill-in-the-blanks' ? response.answers : {};\n const blanks = new Map(item.blanks.map((blank) => [blank.id, blank]));\n return {\n ...base(data),\n activityType: 'fill-in-the-blanks',\n passage: numberedPassage(item.passage),\n blanks: placeholderOrder(item.passage).map((id, index) => {\n const blank = blanks.get(id);\n const detail = byId.get(id);\n const accepted =\n full && Array.isArray(blank?.acceptedAnswers)\n ? [...blank.acceptedAnswers]\n : listOf(detail?.correctResponse);\n return {\n id,\n position: index + 1,\n typed: typeof answers[id] === 'string' ? (answers[id] as string) : '',\n accepted,\n correct: detail === undefined ? null : detailRight(detail),\n ...(typeof blank?.hint === 'string' ? { hint: blank.hint } : {}),\n ...(typeof blank?.feedback === 'string' ? { feedback: blank.feedback } : {}),\n };\n }),\n };\n }\n case 'gap-select': {\n const item = data as unknown as GapSelectData;\n const selections = response?.type === 'gap-select' ? response.selections : {};\n const gaps = new Map(item.gaps.map((gap) => [gap.id, gap]));\n const banks = new Map((item.banks ?? []).map((bank) => [bank.id, bank]));\n return {\n ...base(data),\n activityType: 'gap-select',\n passage: numberedPassage(item.passage),\n gaps: placeholderOrder(item.passage).map((id, index) => {\n const gap = gaps.get(id);\n const choices =\n gap?.choices ??\n (gap?.bankId === undefined ? [] : (banks.get(gap.bankId)?.choices ?? []));\n const textOf = (choiceId: string | undefined): string | null =>\n choiceId === undefined\n ? null\n : (choices.find((choice) => choice.id === choiceId)?.text ?? null);\n const detail = byId.get(id);\n const answerId =\n full && typeof gap?.correctChoiceId === 'string'\n ? gap.correctChoiceId\n : firstString(detail?.correctResponse);\n return {\n id,\n position: index + 1,\n choices: choices.map((choice) => choice.text),\n chosen: textOf(selections[id]),\n answer: textOf(answerId),\n correct: detail === undefined ? null : detailRight(detail),\n ...(typeof gap?.feedback === 'string' ? { feedback: gap.feedback } : {}),\n };\n }),\n };\n }\n case 'dictation': {\n const item = data as unknown as DictationData;\n const typed = response?.type === 'dictation' ? response.text : '';\n const hasKey = full && typeof item.transcript === 'string';\n let words: AiWordFact[] | null = null;\n if (hasKey) {\n words = alignDictation(item, typed).words.map((word) => ({\n expected: word.reference,\n typed: word.attempt,\n status: word.status,\n }));\n } else if (details !== null && details.length > 0) {\n words = details.map((detail) => {\n const expected = firstString(detail.correctResponse) ?? '';\n const attempt = firstString(detail.learnerResponse) ?? '';\n let status: 'correct' | 'incorrect' | 'missing' | 'extra' = 'incorrect';\n if (detailRight(detail)) {\n status = 'correct';\n } else if (attempt === '') {\n status = 'missing';\n } else if (expected === '') {\n status = 'extra';\n }\n return { expected, typed: attempt, status };\n });\n }\n return {\n ...base(data),\n activityType: 'dictation',\n transcript: hasKey ? item.transcript : null,\n typed,\n words,\n };\n }\n default:\n return null;\n }\n}\n\n/** The SDK's own grade of `response`, with its per-part details; `null` when it cannot grade. */\nfunction gradeLocally(\n data: AiActivityInput,\n response: LearnerResponse,\n): { details: ScoringDetail[]; grade: AiGrade } | null {\n if ((data as { redacted?: unknown }).redacted === true) {\n return null;\n }\n try {\n const result = score(data.type as never, data as unknown as ActivityData, response);\n return { details: result.details, grade: aiGradeOf(result) };\n } catch {\n return null;\n }\n}\n\n/**\n * The request for an explanation of `response`, or `null` when there is none\n * to give: an unsupported type, an author who switched explanations off,\n * nothing graded to explain, or a grade of record that cannot be a grade.\n *\n * A scored `outcome` — the grade of record a review shows — wins over grading\n * locally, so the explanation speaks to the grade on the screen. One whose\n * numbers cannot be a grade (the rule `outcomeFromGrade` applies: `maxScore`\n * positive and finite, `score` finite from 0 to it) gets no explanation at all:\n * explaining 85 \"out of 1\" would tell the model a wrong answer was correct, and\n * grading locally instead would speak to a grade the screen does not show.\n */\nexport function aiExplanationRequest(input: {\n data: AiActivityInput;\n response: LearnerResponse | null;\n outcome?: ItemOutcome;\n learnerLocale?: string;\n}): AiExplanationRequest | null {\n const { data, response, outcome, learnerLocale } = input;\n if (!aiSupports(data.type, 'explanation') || !aiAllowedByContent(data, 'explanation')) {\n return null;\n }\n let details: ScoringDetail[] | null = null;\n let grade: AiGrade | null = null;\n if (outcome?.status === 'scored') {\n // Read once, so the check and the grade handed on see the same numbers.\n const { score: recorded, maxScore, passed } = outcome;\n if (!isGradeInRange(recorded, maxScore)) {\n return null;\n }\n details = outcome.details;\n grade = aiGradeOf({ score: recorded, maxScore, passed });\n } else if (response !== null) {\n const local = gradeLocally(data, response);\n if (local !== null) {\n details = local.details;\n grade = local.grade;\n }\n }\n if (grade === null) {\n return null;\n }\n const facts = buildAiFacts(data, response, details);\n if (facts === null) {\n return null;\n }\n return {\n feature: 'explanation',\n facts,\n grade,\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n };\n}\n\n/**\n * The request for the next hint on an unsubmitted answer, or `null` when\n * there is none to give: an unsupported type, an author who switched hints\n * off, or a redacted item — hints are for practice, where the item is whole.\n *\n * The facts carry the key, and which parts the learner has right so far, so a\n * model can hint where it helps. The learner never sees them; a hint that\n * contains an answer is refused by {@link checkAiHint}.\n */\nexport function aiHintRequest(input: {\n data: AiActivityInput;\n response: LearnerResponse | null;\n previousHints: readonly string[];\n learnerLocale?: string;\n}): AiHintRequest | null {\n const { data, previousHints, learnerLocale } = input;\n if (\n !aiSupports(data.type, 'hint') ||\n !aiAllowedByContent(data, 'hint') ||\n (data as { redacted?: unknown }).redacted === true\n ) {\n return null;\n }\n const response = input.response ?? emptyResponse(data.type);\n const local = response === null ? null : gradeLocally(data, response);\n const facts = buildAiFacts(data, response, local?.details ?? null);\n if (facts === null) {\n return null;\n }\n return {\n feature: 'hint',\n facts,\n hintNumber: previousHints.length + 1,\n previousHints: [...previousHints],\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n };\n}\n\n// ── Checking what comes back ───────────────────────────────────────────\n\nconst VERDICTS: readonly string[] = ['correct', 'partly-correct', 'incorrect'];\n\n/** A provenance field a host may send, kept only when it is a short string. */\nfunction provenanceOf(raw: unknown): AiProvenance | undefined {\n if (typeof raw !== 'object' || raw === null) {\n return undefined;\n }\n const kept: AiProvenance = {};\n for (const key of ['model', 'promptHash', 'generatedAt'] as const) {\n const value = (raw as Record<string, unknown>)[key];\n if (typeof value === 'string' && value.length > 0 && value.length <= 200) {\n kept[key] = value;\n }\n }\n return Object.keys(kept).length > 0 ? kept : undefined;\n}\n\n/**\n * What the call cost, as a port reported it. Read on the same terms as the\n * provenance: a number that is not finite and zero or more says nothing about\n * a cost, so it is dropped rather than carried into a host's totals.\n */\nfunction usageOf(raw: unknown): GraderUsage | undefined {\n if (typeof raw !== 'object' || raw === null) {\n return undefined;\n }\n const kept: GraderUsage = {};\n for (const key of ['promptTokens', 'completionTokens', 'costUsd'] as const) {\n const value = (raw as Record<string, unknown>)[key];\n if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {\n kept[key] = value;\n }\n }\n return Object.keys(kept).length > 0 ? kept : undefined;\n}\n\n/**\n * Reads what a port returned. Anything but an object with a string `text` is\n * `malformed`; text that is empty once cleaned, or longer than\n * {@link AI_TEXT_MAX_LENGTH}, is refused rather than shown or cut.\n */\nexport function readAiTextResult(\n raw: unknown,\n): { ok: true; result: AiTextResult } | { ok: false; refusal: AiRefusal } {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n return { ok: false, refusal: 'malformed' };\n }\n const record = raw as Record<string, unknown>;\n if (typeof record.text !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n if (record.verdict !== undefined && !VERDICTS.includes(record.verdict as string)) {\n return { ok: false, refusal: 'malformed' };\n }\n const text = cleanText(record.text);\n if (text === '') {\n return { ok: false, refusal: 'empty' };\n }\n if ([...text].length > AI_TEXT_MAX_LENGTH) {\n return { ok: false, refusal: 'too-long' };\n }\n const provenance = provenanceOf(record.provenance);\n const usage = usageOf(record.usage);\n return {\n ok: true,\n result: {\n text,\n ...(record.verdict !== undefined ? { verdict: record.verdict as AiVerdict } : {}),\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n\n/**\n * Checks an explanation a port returned for `request`. On top of\n * {@link readAiTextResult}: an explanation that states a verdict other than\n * the SDK's grade is refused (`contradicts-grade`) — it explains an answer\n * the learner did not get.\n */\nexport function checkAiExplanation(\n raw: unknown,\n request: AiExplanationRequest,\n): { ok: true; result: AiTextResult } | { ok: false; refusal: AiRefusal } {\n const read = readAiTextResult(raw);\n if (!read.ok) {\n return read;\n }\n if (read.result.verdict !== undefined && read.result.verdict !== request.grade.category) {\n return { ok: false, refusal: 'contradicts-grade' };\n }\n return read;\n}\n\n/**\n * Checks a hint a port returned for `request`. On top of\n * {@link readAiTextResult}: a hint that contains an answer is refused\n * (`reveals-answer`) — see {@link hintRevealsAnswer}.\n */\nexport function checkAiHint(\n raw: unknown,\n request: AiHintRequest,\n): { ok: true; result: AiTextResult } | { ok: false; refusal: AiRefusal } {\n const read = readAiTextResult(raw);\n if (!read.ok) {\n return read;\n }\n if (hintRevealsAnswer(request.facts, read.result.text)) {\n return { ok: false, refusal: 'reveals-answer' };\n }\n return read;\n}\n\n// ── The answer-leak guard ──────────────────────────────────────────────\n\n/**\n * Text folded for searching: compatibility forms read as what they stand for,\n * accents and other marks removed, lower-cased, and every run of anything but\n * letters and digits turned into one space. \"Está,\" and \"esta\" fold alike.\n */\nfunction fold(text: string): string[] {\n const folded = text\n .normalize('NFKD')\n .replace(/\\p{M}/gu, '')\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, ' ')\n .trim();\n return folded === '' ? [] : folded.split(' ');\n}\n\n/** Whether `needle` appears in `haystack` as a run of whole words. */\nfunction containsWords(haystack: readonly string[], needle: readonly string[]): boolean {\n if (needle.length === 0 || needle.length > haystack.length) {\n return false;\n }\n for (let start = 0; start + needle.length <= haystack.length; start += 1) {\n let match = true;\n for (let offset = 0; offset < needle.length; offset += 1) {\n if (haystack[start + offset] !== needle[offset]) {\n match = false;\n break;\n }\n }\n if (match) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * An answer too short to search for on its own: one word of fewer than four\n * characters — \"is\", \"the\", \"26\". A hint may use such a word freely; it\n * reveals the answer only when it writes it where the answer goes.\n */\nconst isShort = (words: readonly string[]): boolean =>\n words.length === 1 && [...(words[0] as string)].length < 4;\n\n/**\n * The words either side of the gap numbered `position` in a numbered\n * passage, other gaps left out.\n */\nfunction neighbours(passage: string, position: number): { before?: string; after?: string } {\n const marker = `[${position}]`;\n const at = passage.indexOf(marker);\n if (at === -1) {\n return {};\n }\n const strip = (text: string): string => text.replace(/\\[\\d+\\]/g, ' ');\n const left = fold(strip(passage.slice(0, at)));\n const right = fold(strip(passage.slice(at + marker.length)));\n const before = left[left.length - 1];\n const after = right[0];\n return {\n ...(before !== undefined ? { before } : {}),\n ...(after !== undefined ? { after } : {}),\n };\n}\n\n/**\n * Whether a hint gives away an answer, judged against the facts it was asked\n * for. It is a floor, not a proof: it finds an answer written out, not one\n * spelled letter by letter or described.\n *\n * - **Multiple choice:** the text of any correct option, as whole words.\n * - **Fill-in-the-blanks and gap select:** any accepted answer, as whole words.\n * An answer of one short word (\"is\", \"the\") counts only where the hint\n * writes it beside a neighbour it has in the passage — \"name is\", \"is\n * Rossi\" — so a hint may still use the word on its own.\n *\n * Case, accents and punctuation are ignored on both sides.\n */\nexport function hintRevealsAnswer(facts: AiItemFacts, hint: string): boolean {\n const words = fold(hint);\n const reveals = (answer: string, passage?: string, position?: number): boolean => {\n const needle = fold(answer);\n if (needle.length === 0) {\n return false;\n }\n if (!isShort(needle) || passage === undefined || position === undefined) {\n return containsWords(words, needle);\n }\n const { before, after } = neighbours(passage, position);\n if (before === undefined && after === undefined) {\n return containsWords(words, needle);\n }\n return (\n (before !== undefined && containsWords(words, [before, ...needle])) ||\n (after !== undefined && containsWords(words, [...needle, after]))\n );\n };\n switch (facts.activityType) {\n case 'multiple-choice':\n return facts.options.some((option) => option.correct === true && reveals(option.text));\n case 'fill-in-the-blanks':\n return facts.blanks.some((blank) =>\n (blank.accepted ?? []).some((answer) => reveals(answer, facts.passage, blank.position)),\n );\n case 'gap-select':\n return facts.gaps.some(\n (gap) => gap.answer !== null && reveals(gap.answer, facts.passage, gap.position),\n );\n case 'dictation':\n // Never asked: a dictation takes no AI hints (AI_HINT_TYPES).\n return false;\n default:\n return false;\n }\n}\n","import { aiAllowedByContent, aiSupports, readAiTextResult } from './ai.js';\nimport { cleanText } from './ai-text.js';\nimport { countWords } from './count-words.js';\nimport { gradeFromRubric } from './grading.js';\nimport type { LearnerResponse, WrittenResponseData } from './types/activity.js';\nimport type {\n AiActivityInput,\n AiRefusal,\n AiRubricCriterionFact,\n AiWritingFeedback,\n AiWritingFeedbackRequest,\n} from './types/ai.js';\nimport type { CriterionScore, InlineCorrection } from './types/grading.js';\n\n/** The most corrections one piece of writing feedback may carry. More is refused, not cut. */\nexport const AI_WRITING_MAX_CORRECTIONS = 20;\n\n/** The longest quote, correction, explanation or criterion comment, in code points. */\nexport const AI_WRITING_MAX_FIELD_LENGTH = 500;\n\n/** The longest `category` or `band`: a tag, not a sentence. */\nconst MAX_TAG_LENGTH = 40;\n\n/**\n * The request for feedback on a draft, or `null` when there is none to give:\n * not a written response, an author who switched explanations off (writing\n * feedback reads `ai.explanations`), a redacted item — feedback is for\n * practice, where the item is whole — or a draft with nothing in it.\n *\n * The facts are the task as the learner saw it, the draft verbatim with its\n * word count recomputed as the grader will count it, and the rubric the answer\n * will be graded on. `previousFeedback` is the text of the feedback already\n * shown on earlier drafts, so a model can speak to what changed.\n */\nexport function aiWritingFeedbackRequest(input: {\n data: AiActivityInput;\n response: LearnerResponse | null;\n previousFeedback?: readonly string[];\n learnerLocale?: string;\n}): AiWritingFeedbackRequest | null {\n const { data, response, learnerLocale } = input;\n if (\n !aiSupports(data.type, 'writing-feedback') ||\n !aiAllowedByContent(data, 'writing-feedback') ||\n (data as { redacted?: unknown }).redacted === true\n ) {\n return null;\n }\n const text = response?.type === 'written-response' ? response.text : '';\n if (typeof text !== 'string' || text.trim() === '') {\n return null;\n }\n const item = data as unknown as WrittenResponseData;\n const wordCount = countWords(text);\n const minWords = typeof item.minWords === 'number' ? item.minWords : 0;\n const maxWords = typeof item.maxWords === 'number' ? item.maxWords : Number.POSITIVE_INFINITY;\n const criteria = Array.isArray(item.rubric?.criteria) ? item.rubric.criteria : null;\n const rubric: AiRubricCriterionFact[] | null =\n criteria === null || criteria.length === 0\n ? null\n : criteria.map((criterion) => ({\n name: criterion.name,\n ...(typeof criterion.description === 'string'\n ? { description: criterion.description }\n : {}),\n weight: criterion.weight,\n }));\n const previousFeedback = [...(input.previousFeedback ?? [])];\n return {\n feature: 'writing-feedback',\n facts: {\n activityType: 'written-response',\n activityId: data.id,\n title: data.title,\n ...(typeof data.locale === 'string' ? { locale: data.locale } : {}),\n prompt: typeof item.prompt === 'string' ? item.prompt : '',\n text,\n wordCount,\n minWords,\n maxWords,\n withinWordBounds: wordCount >= minWords && wordCount <= maxWords,\n rubric,\n ...(typeof item.languageTarget === 'string' ? { languageTarget: item.languageTarget } : {}),\n },\n draftNumber: previousFeedback.length + 1,\n previousFeedback,\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n };\n}\n\n// ── Finding a quote in the draft ───────────────────────────────────────\n\n/** Typographic quotes a model writes for the plain ones a learner typed, and back. */\nconst QUOTE_FOLD: Readonly<Record<string, string>> = {\n '‘': \"'\",\n '’': \"'\",\n '‛': \"'\",\n '′': \"'\",\n '“': '\"',\n '”': '\"',\n '„': '\"',\n '″': '\"',\n};\n\n/**\n * Text as a quote is matched against it: typographic quotes folded to plain\n * ones and every run of whitespace one space — the two liberties a model takes\n * when it copies words out — with, for each folded character, where it came\n * from, so a match maps back to the learner's own text. Case is kept: a\n * correction of \"i\" to \"I\" is about case.\n */\nfunction folded(text: string): { chars: string; from: number[] } {\n let chars = '';\n const from: number[] = [];\n let space = false;\n for (let at = 0; at < text.length; at += 1) {\n const char = text[at] as string;\n if (/\\s/u.test(char)) {\n if (!space) {\n chars += ' ';\n from.push(at);\n space = true;\n }\n continue;\n }\n space = false;\n chars += QUOTE_FOLD[char] ?? char;\n from.push(at);\n }\n return { chars, from };\n}\n\n/** A quote as it is searched for: folded, and without the spaces around it. */\nconst needle = (quote: string): string => folded(quote).chars.trim();\n\n/**\n * Where `quote` sits in the draft: at `claimed`, when the model named a range —\n * which must then hold the quote — or else at its first occurrence not already\n * taken by an identical quote, so a mistake made twice is anchored twice. A\n * quote corrected more often than it occurs takes its first occurrence again.\n * `null` when the draft does not contain it where claimed, or at all.\n */\nfunction anchor(\n draft: { text: string; chars: string; from: number[] },\n quote: string,\n claimed: { start: number; end: number } | undefined,\n taken: Set<string>,\n): { start: number; end: number } | null {\n const sought = needle(quote);\n if (sought === '') {\n return null;\n }\n if (claimed !== undefined) {\n const { start, end } = claimed;\n if (\n !Number.isSafeInteger(start) ||\n !Number.isSafeInteger(end) ||\n start < 0 ||\n end > draft.text.length ||\n start >= end\n ) {\n return null;\n }\n const span = draft.text.slice(start, end);\n if (needle(span) !== sought) {\n return null;\n }\n // The span, without whitespace the model's range took in at either end.\n const lead = span.length - span.trimStart().length;\n const trail = span.length - span.trimEnd().length;\n return { start: start + lead, end: end - trail };\n }\n let first: { start: number; end: number } | null = null;\n for (let at = draft.chars.indexOf(sought); at !== -1; at = draft.chars.indexOf(sought, at + 1)) {\n const start = draft.from[at] as number;\n const end = (draft.from[at + sought.length - 1] as number) + 1;\n const found = { start, end };\n first ??= found;\n const key = `${start}:${end}`;\n if (!taken.has(key)) {\n taken.add(key);\n return found;\n }\n }\n return first;\n}\n\n// ── Checking what comes back ───────────────────────────────────────────\n\n/** A field that must be a string, at most `max` code points once cleaned; `undefined` when absent. */\nfunction textField(\n value: unknown,\n max: number,\n): { ok: true; value: string | undefined } | { ok: false; refusal: AiRefusal } {\n if (value === undefined || value === null) {\n return { ok: true, value: undefined };\n }\n if (typeof value !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n const cleaned = cleanText(value);\n if ([...cleaned].length > max) {\n return { ok: false, refusal: 'too-long' };\n }\n return { ok: true, value: cleaned === '' ? undefined : cleaned };\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n/** The corrections, each anchored in the draft, or why the reply is refused. */\nfunction checkCorrections(\n raw: unknown,\n text: string,\n): { ok: true; corrections: InlineCorrection[] } | { ok: false; refusal: AiRefusal } {\n if (raw === undefined || raw === null) {\n return { ok: true, corrections: [] };\n }\n if (!Array.isArray(raw)) {\n return { ok: false, refusal: 'malformed' };\n }\n if (raw.length > AI_WRITING_MAX_CORRECTIONS) {\n return { ok: false, refusal: 'too-long' };\n }\n const draft = { text, ...folded(text) };\n const taken = new Set<string>();\n const corrections: InlineCorrection[] = [];\n for (const entry of raw) {\n if (!isRecord(entry) || typeof entry.original !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n if (typeof entry.corrected !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n if (\n [...entry.original].length > AI_WRITING_MAX_FIELD_LENGTH ||\n [...entry.corrected].length > AI_WRITING_MAX_FIELD_LENGTH\n ) {\n return { ok: false, refusal: 'too-long' };\n }\n // A correction that changes nothing corrects nothing.\n if (needle(entry.original) === needle(entry.corrected)) {\n return { ok: false, refusal: 'malformed' };\n }\n const explanation = textField(entry.explanation, AI_WRITING_MAX_FIELD_LENGTH);\n const category = textField(entry.category, MAX_TAG_LENGTH);\n if (!explanation.ok) {\n return explanation;\n }\n if (!category.ok) {\n return category;\n }\n let claimed: { start: number; end: number } | undefined;\n if (entry.range !== undefined && entry.range !== null) {\n if (!isRecord(entry.range)) {\n return { ok: false, refusal: 'malformed' };\n }\n claimed = { start: entry.range.start as number, end: entry.range.end as number };\n }\n const at = anchor(draft, entry.original, claimed, taken);\n if (at === null) {\n return { ok: false, refusal: 'misquotes-answer' };\n }\n corrections.push({\n original: text.slice(at.start, at.end),\n corrected: cleanText(entry.corrected),\n ...(explanation.value !== undefined ? { explanation: explanation.value } : {}),\n ...(category.value !== undefined ? { category: category.value } : {}),\n range: at,\n });\n }\n return { ok: true, corrections };\n}\n\n/** A criterion score a grade could be computed from: finite, 0 or more, and not above its maximum. */\nfunction isJudgedScore(score: unknown, maxScore: unknown): boolean {\n if (score === undefined) {\n return maxScore === undefined || (typeof maxScore === 'number' && maxScore > 0);\n }\n const max = maxScore ?? 1;\n return (\n typeof score === 'number' &&\n typeof max === 'number' &&\n Number.isFinite(score) &&\n Number.isFinite(max) &&\n max > 0 &&\n score >= 0 &&\n score - max <= max * 1e-9\n );\n}\n\n/** The judgements, each on a criterion the author wrote and weighted as the author weighted it. */\nfunction checkCriteria(\n raw: unknown,\n rubric: readonly AiRubricCriterionFact[] | null,\n): { ok: true; criteria: CriterionScore[] } | { ok: false; refusal: AiRefusal } {\n if (raw === undefined || raw === null) {\n return { ok: true, criteria: [] };\n }\n if (!Array.isArray(raw)) {\n return { ok: false, refusal: 'malformed' };\n }\n if (raw.length === 0) {\n return { ok: true, criteria: [] };\n }\n // Judging a criterion the author did not write is judging a different task.\n if (rubric === null) {\n return { ok: false, refusal: 'malformed' };\n }\n const weights = new Map(rubric.map((criterion) => [criterion.name, criterion.weight]));\n const seen = new Set<string>();\n const criteria: CriterionScore[] = [];\n for (const entry of raw) {\n if (!isRecord(entry) || typeof entry.name !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n const weight = weights.get(entry.name);\n if (weight === undefined || seen.has(entry.name)) {\n return { ok: false, refusal: 'malformed' };\n }\n seen.add(entry.name);\n if (!isJudgedScore(entry.score, entry.maxScore)) {\n return { ok: false, refusal: 'malformed' };\n }\n if (entry.notApplicable !== undefined && typeof entry.notApplicable !== 'boolean') {\n return { ok: false, refusal: 'malformed' };\n }\n const band = textField(entry.band, MAX_TAG_LENGTH);\n const comment = textField(entry.comment, AI_WRITING_MAX_FIELD_LENGTH);\n if (!band.ok) {\n return band;\n }\n if (!comment.ok) {\n return comment;\n }\n criteria.push({\n name: entry.name,\n ...(typeof entry.score === 'number' ? { score: entry.score } : {}),\n ...(typeof entry.maxScore === 'number' ? { maxScore: entry.maxScore } : {}),\n ...(band.value !== undefined ? { band: band.value } : {}),\n ...(comment.value !== undefined ? { comment: comment.value } : {}),\n ...(entry.notApplicable === true ? { notApplicable: true } : {}),\n weight,\n });\n }\n return { ok: true, criteria };\n}\n\n/**\n * Checks writing feedback a port returned for `request`, and turns it into what\n * a learner is shown. The SDK shows a model's feedback whole or not at all: it\n * never drops the part it doubts and shows the rest.\n *\n * - **The text** is read as every AI text is (`readAiTextResult`): plain text,\n * not empty, at most {@link AI_TEXT_MAX_LENGTH} code points.\n * - **Every correction must quote the draft** — the check only this SDK can run.\n * The SDK finds each quote in the draft itself, typographic quotes and runs of\n * whitespace aside, and anchors it there; a range the model claims must hold\n * the quote. A correction of words the learner did not write refuses the\n * whole reply as `misquotes-answer`, as a hint that gives the answer away\n * refuses the hint. The `original` shown is the draft's own text at the range.\n * At most {@link AI_WRITING_MAX_CORRECTIONS}; a correction that changes\n * nothing is `malformed`.\n * - **Criteria are judgements; the arithmetic is the SDK's.** Each names a\n * criterion of the item's rubric, once; its score, when it has one, is a\n * score out of its `maxScore`. The rubric's weights are attached, whatever\n * the model sent. Anything else is `malformed`, as is any criterion on an\n * item with no rubric.\n * - **`indicativeScore`** is the rubric's weighted total, computed as\n * `gradeFromRubric` computes a grade, when every criterion of the rubric was\n * judged; `null` otherwise. An indication, never a grade.\n */\nexport function checkAiWritingFeedback(\n raw: unknown,\n request: AiWritingFeedbackRequest,\n): { ok: true; feedback: AiWritingFeedback } | { ok: false; refusal: AiRefusal } {\n const read = readAiTextResult(raw);\n if (!read.ok) {\n return read;\n }\n const record = raw as Record<string, unknown>;\n const corrections = checkCorrections(record.corrections, request.facts.text);\n if (!corrections.ok) {\n return corrections;\n }\n const rubric = request.facts.rubric;\n const criteria = checkCriteria(record.criteria, rubric);\n if (!criteria.ok) {\n return criteria;\n }\n let indicativeScore: number | null = null;\n const judged = new Set(criteria.criteria.map((criterion) => criterion.name));\n if (rubric?.every((criterion) => judged.has(criterion.name))) {\n const grade = gradeFromRubric(criteria.criteria);\n indicativeScore = 'unscorable' in grade ? null : grade.score;\n }\n const { text, provenance, usage } = read.result;\n return {\n ok: true,\n feedback: {\n text,\n corrections: corrections.corrections,\n criteria: criteria.criteria,\n indicativeScore,\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;AAUO,SAAS,UAAU,MAAsB;AAC9C,SACE,KACG,QAAQ,UAAU,IAAI,EAEtB,QAAQ,sEAAsE,EAAE,EAChF,QAAQ,WAAW,MAAM,EACzB,KAAK;AAEZ;;;ACiBO,IAAM,uBAA2D;AAAA,EACtE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACO,IAAM,gBAAoD;AAAA,EAC/D;AAAA,EACA;AAAA,EACA;AACF;AAGO,IAAM,qBAAqB;AAO3B,SAAS,WAAW,cAAsB,SAA6B;AAC5E,MAAI,YAAY,oBAAoB;AAClC,WAAO,iBAAiB;AAAA,EAC1B;AACA,QAAM,QAAQ,YAAY,SAAS,gBAAgB;AACnD,SAAQ,MAA4B,SAAS,YAAY;AAC3D;AAUO,SAAS,mBAAmB,MAAuB,SAA6B;AACrF,QAAM,cAAe,KAA0B;AAC/C,MAAI,OAAO,gBAAgB,YAAY,gBAAgB,MAAM;AAC3D,WAAO;AAAA,EACT;AACA,QAAM,MAAM,YAAY,SAAS,UAAU;AAC3C,SAAQ,YAAwC,GAAG,MAAM;AAC3D;AAGO,SAAS,UAAU,QAAuE;AAC/F,QAAM,QAAQ,OAAO,WAAW,IAAI,OAAO,QAAQ,OAAO,WAAW;AACrE,MAAI,WAAsB;AAC1B,MAAI,SAAS,IAAI,MAAM;AACrB,eAAW;AAAA,EACb,WAAW,SAAS,MAAM;AACxB,eAAW;AAAA,EACb;AACA,SAAO,EAAE,OAAO,OAAO,OAAO,UAAU,OAAO,UAAU,QAAQ,OAAO,QAAQ,SAAS;AAC3F;AAQA,SAAS,iBAAiB,SAA2B;AACnD,SAAO,CAAC,GAAG,QAAQ,SAAS,cAAc,CAAC,EAAE,IAAI,CAAC,UAAU,MAAM,CAAC,CAAW;AAChF;AAGA,SAAS,gBAAgB,SAAyB;AAChD,MAAI,WAAW;AACf,SAAO,QAAQ,QAAQ,gBAAgB,MAAM;AAC3C,gBAAY;AACZ,WAAO,IAAI,QAAQ;AAAA,EACrB,CAAC;AACH;AAOA,IAAM,gBAAgB,CAAC,WACpB,OAAiC,YAAY;AAGhD,SAAS,YAAY,QAAgC;AACnD,QAAM,UAAmB,OAAO;AAChC,SAAO,YAAY,SAAY,cAAc,MAAM,IAAI,YAAY;AACrE;AAGA,SAAS,eAAe,QAAuB,QAA0B;AACvE,UAAQ,OAAO,SAAS;AAAA,IACtB,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO,SAAS,cAAc,MAAM,IAAI,CAAC,cAAc,MAAM;AAAA,EACjE;AACF;AAEA,IAAM,cAAc,CAAC,UACnB,MAAM,QAAQ,KAAK,IAAI,MAAM,CAAC,IAAI;AAEpC,IAAM,SAAS,CAAC,UACd,UAAU,SAAY,OAAO,MAAM,QAAQ,KAAK,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,KAAK;AAGzE,SAAS,cAAc,MAAsC;AAC3D,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,EAAE,MAAM,mBAAmB,mBAAmB,CAAC,EAAE;AAAA,IAC1D,KAAK;AACH,aAAO,EAAE,MAAM,sBAAsB,SAAS,CAAC,EAAE;AAAA,IACnD,KAAK;AACH,aAAO,EAAE,MAAM,cAAc,YAAY,CAAC,EAAE;AAAA,IAC9C,KAAK;AACH,aAAO,EAAE,MAAM,aAAa,MAAM,GAAG;AAAA,IACvC;AACE,aAAO;AAAA,EACX;AACF;AAEA,IAAM,OAAO,CAAC,UAA2B;AAAA,EACvC,YAAY,KAAK;AAAA,EACjB,OAAO,KAAK;AAAA,EACZ,GAAI,OAAO,KAAK,WAAW,WAAW,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AACnE;AAWO,SAAS,aACd,MACA,UACA,SACoB;AACpB,QAAM,OAAO,IAAI,KAAK,WAAW,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,QAAQ,MAAM,CAAC,CAAC;AAC7E,QAAM,OAAQ,KAAgC,aAAa;AAC3D,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK,mBAAmB;AACtB,YAAM,OAAO;AACb,YAAM,SAAS,IAAI;AAAA,QACjB,UAAU,SAAS,oBAAoB,SAAS,oBAAoB,CAAC;AAAA,MACvE;AACA,aAAO;AAAA,QACL,GAAG,KAAK,IAAI;AAAA,QACZ,cAAc;AAAA,QACd,UAAU,KAAK;AAAA,QACf,MAAM,KAAK;AAAA,QACX,SAAS,KAAK,QAAQ,IAAI,CAAC,WAAW;AACpC,gBAAM,SAAS,KAAK,IAAI,OAAO,EAAE;AACjC,gBAAM,YAAY,OAAO,IAAI,OAAO,EAAE;AACtC,cAAI,UAA0B;AAC9B,cAAI,QAAQ,OAAO,OAAO,cAAc,WAAW;AACjD,sBAAU,OAAO;AAAA,UACnB,WAAW,WAAW,QAAW;AAC/B,sBAAU,eAAe,QAAQ,SAAS;AAAA,UAC5C;AACA,iBAAO;AAAA,YACL,IAAI,OAAO;AAAA,YACX,MAAM,OAAO;AAAA,YACb,QAAQ;AAAA,YACR;AAAA,YACA,GAAI,OAAO,OAAO,aAAa,WAAW,EAAE,UAAU,OAAO,SAAS,IAAI,CAAC;AAAA,UAC7E;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,KAAK,sBAAsB;AACzB,YAAM,OAAO;AACb,YAAM,UAAU,UAAU,SAAS,uBAAuB,SAAS,UAAU,CAAC;AAC9E,YAAM,SAAS,IAAI,IAAI,KAAK,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;AACpE,aAAO;AAAA,QACL,GAAG,KAAK,IAAI;AAAA,QACZ,cAAc;AAAA,QACd,SAAS,gBAAgB,KAAK,OAAO;AAAA,QACrC,QAAQ,iBAAiB,KAAK,OAAO,EAAE,IAAI,CAAC,IAAI,UAAU;AACxD,gBAAM,QAAQ,OAAO,IAAI,EAAE;AAC3B,gBAAM,SAAS,KAAK,IAAI,EAAE;AAC1B,gBAAM,WACJ,QAAQ,MAAM,QAAQ,OAAO,eAAe,IACxC,CAAC,GAAG,MAAM,eAAe,IACzB,OAAO,QAAQ,eAAe;AACpC,iBAAO;AAAA,YACL;AAAA,YACA,UAAU,QAAQ;AAAA,YAClB,OAAO,OAAO,QAAQ,EAAE,MAAM,WAAY,QAAQ,EAAE,IAAe;AAAA,YACnE;AAAA,YACA,SAAS,WAAW,SAAY,OAAO,YAAY,MAAM;AAAA,YACzD,GAAI,OAAO,OAAO,SAAS,WAAW,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,YAC9D,GAAI,OAAO,OAAO,aAAa,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;AAAA,UAC5E;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,KAAK,cAAc;AACjB,YAAM,OAAO;AACb,YAAM,aAAa,UAAU,SAAS,eAAe,SAAS,aAAa,CAAC;AAC5E,YAAM,OAAO,IAAI,IAAI,KAAK,KAAK,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC;AAC1D,YAAM,QAAQ,IAAI,KAAK,KAAK,SAAS,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AACvE,aAAO;AAAA,QACL,GAAG,KAAK,IAAI;AAAA,QACZ,cAAc;AAAA,QACd,SAAS,gBAAgB,KAAK,OAAO;AAAA,QACrC,MAAM,iBAAiB,KAAK,OAAO,EAAE,IAAI,CAAC,IAAI,UAAU;AACtD,gBAAM,MAAM,KAAK,IAAI,EAAE;AACvB,gBAAM,UACJ,KAAK,YACJ,KAAK,WAAW,SAAY,CAAC,IAAK,MAAM,IAAI,IAAI,MAAM,GAAG,WAAW,CAAC;AACxE,gBAAM,SAAS,CAAC,aACd,aAAa,SACT,OACC,QAAQ,KAAK,CAAC,WAAW,OAAO,OAAO,QAAQ,GAAG,QAAQ;AACjE,gBAAM,SAAS,KAAK,IAAI,EAAE;AAC1B,gBAAM,WACJ,QAAQ,OAAO,KAAK,oBAAoB,WACpC,IAAI,kBACJ,YAAY,QAAQ,eAAe;AACzC,iBAAO;AAAA,YACL;AAAA,YACA,UAAU,QAAQ;AAAA,YAClB,SAAS,QAAQ,IAAI,CAAC,WAAW,OAAO,IAAI;AAAA,YAC5C,QAAQ,OAAO,WAAW,EAAE,CAAC;AAAA,YAC7B,QAAQ,OAAO,QAAQ;AAAA,YACvB,SAAS,WAAW,SAAY,OAAO,YAAY,MAAM;AAAA,YACzD,GAAI,OAAO,KAAK,aAAa,WAAW,EAAE,UAAU,IAAI,SAAS,IAAI,CAAC;AAAA,UACxE;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,KAAK,aAAa;AAChB,YAAM,OAAO;AACb,YAAM,QAAQ,UAAU,SAAS,cAAc,SAAS,OAAO;AAC/D,YAAM,SAAS,QAAQ,OAAO,KAAK,eAAe;AAClD,UAAI,QAA6B;AACjC,UAAI,QAAQ;AACV,gBAAQ,eAAe,MAAM,KAAK,EAAE,MAAM,IAAI,CAAC,UAAU;AAAA,UACvD,UAAU,KAAK;AAAA,UACf,OAAO,KAAK;AAAA,UACZ,QAAQ,KAAK;AAAA,QACf,EAAE;AAAA,MACJ,WAAW,YAAY,QAAQ,QAAQ,SAAS,GAAG;AACjD,gBAAQ,QAAQ,IAAI,CAAC,WAAW;AAC9B,gBAAM,WAAW,YAAY,OAAO,eAAe,KAAK;AACxD,gBAAM,UAAU,YAAY,OAAO,eAAe,KAAK;AACvD,cAAI,SAAwD;AAC5D,cAAI,YAAY,MAAM,GAAG;AACvB,qBAAS;AAAA,UACX,WAAW,YAAY,IAAI;AACzB,qBAAS;AAAA,UACX,WAAW,aAAa,IAAI;AAC1B,qBAAS;AAAA,UACX;AACA,iBAAO,EAAE,UAAU,OAAO,SAAS,OAAO;AAAA,QAC5C,CAAC;AAAA,MACH;AACA,aAAO;AAAA,QACL,GAAG,KAAK,IAAI;AAAA,QACZ,cAAc;AAAA,QACd,YAAY,SAAS,KAAK,aAAa;AAAA,QACvC;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA;AACE,aAAO;AAAA,EACX;AACF;AAGA,SAAS,aACP,MACA,UACqD;AACrD,MAAK,KAAgC,aAAa,MAAM;AACtD,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,SAAS,MAAM,KAAK,MAAe,MAAiC,QAAQ;AAClF,WAAO,EAAE,SAAS,OAAO,SAAS,OAAO,UAAU,MAAM,EAAE;AAAA,EAC7D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAcO,SAAS,qBAAqB,OAKL;AAC9B,QAAM,EAAE,MAAM,UAAU,SAAS,cAAc,IAAI;AACnD,MAAI,CAAC,WAAW,KAAK,MAAM,aAAa,KAAK,CAAC,mBAAmB,MAAM,aAAa,GAAG;AACrF,WAAO;AAAA,EACT;AACA,MAAI,UAAkC;AACtC,MAAI,QAAwB;AAC5B,MAAI,SAAS,WAAW,UAAU;AAEhC,UAAM,EAAE,OAAO,UAAU,UAAU,OAAO,IAAI;AAC9C,QAAI,CAAC,eAAe,UAAU,QAAQ,GAAG;AACvC,aAAO;AAAA,IACT;AACA,cAAU,QAAQ;AAClB,YAAQ,UAAU,EAAE,OAAO,UAAU,UAAU,OAAO,CAAC;AAAA,EACzD,WAAW,aAAa,MAAM;AAC5B,UAAM,QAAQ,aAAa,MAAM,QAAQ;AACzC,QAAI,UAAU,MAAM;AAClB,gBAAU,MAAM;AAChB,cAAQ,MAAM;AAAA,IAChB;AAAA,EACF;AACA,MAAI,UAAU,MAAM;AAClB,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,aAAa,MAAM,UAAU,OAAO;AAClD,MAAI,UAAU,MAAM;AAClB,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA,GAAI,kBAAkB,SAAY,EAAE,cAAc,IAAI,CAAC;AAAA,EACzD;AACF;AAWO,SAAS,cAAc,OAKL;AACvB,QAAM,EAAE,MAAM,eAAe,cAAc,IAAI;AAC/C,MACE,CAAC,WAAW,KAAK,MAAM,MAAM,KAC7B,CAAC,mBAAmB,MAAM,MAAM,KAC/B,KAAgC,aAAa,MAC9C;AACA,WAAO;AAAA,EACT;AACA,QAAM,WAAW,MAAM,YAAY,cAAc,KAAK,IAAI;AAC1D,QAAM,QAAQ,aAAa,OAAO,OAAO,aAAa,MAAM,QAAQ;AACpE,QAAM,QAAQ,aAAa,MAAM,UAAU,OAAO,WAAW,IAAI;AACjE,MAAI,UAAU,MAAM;AAClB,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA,YAAY,cAAc,SAAS;AAAA,IACnC,eAAe,CAAC,GAAG,aAAa;AAAA,IAChC,GAAI,kBAAkB,SAAY,EAAE,cAAc,IAAI,CAAC;AAAA,EACzD;AACF;AAIA,IAAM,WAA8B,CAAC,WAAW,kBAAkB,WAAW;AAG7E,SAAS,aAAa,KAAwC;AAC5D,MAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM;AAC3C,WAAO;AAAA,EACT;AACA,QAAM,OAAqB,CAAC;AAC5B,aAAW,OAAO,CAAC,SAAS,cAAc,aAAa,GAAY;AACjE,UAAM,QAAS,IAAgC,GAAG;AAClD,QAAI,OAAO,UAAU,YAAY,MAAM,SAAS,KAAK,MAAM,UAAU,KAAK;AACxE,WAAK,GAAG,IAAI;AAAA,IACd;AAAA,EACF;AACA,SAAO,OAAO,KAAK,IAAI,EAAE,SAAS,IAAI,OAAO;AAC/C;AAOA,SAAS,QAAQ,KAAuC;AACtD,MAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM;AAC3C,WAAO;AAAA,EACT;AACA,QAAM,OAAoB,CAAC;AAC3B,aAAW,OAAO,CAAC,gBAAgB,oBAAoB,SAAS,GAAY;AAC1E,UAAM,QAAS,IAAgC,GAAG;AAClD,QAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,KAAK,SAAS,GAAG;AACrE,WAAK,GAAG,IAAI;AAAA,IACd;AAAA,EACF;AACA,SAAO,OAAO,KAAK,IAAI,EAAE,SAAS,IAAI,OAAO;AAC/C;AAOO,SAAS,iBACd,KACwE;AACxE,MAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,MAAM,QAAQ,GAAG,GAAG;AACjE,WAAO,EAAE,IAAI,OAAO,SAAS,YAAY;AAAA,EAC3C;AACA,QAAM,SAAS;AACf,MAAI,OAAO,OAAO,SAAS,UAAU;AACnC,WAAO,EAAE,IAAI,OAAO,SAAS,YAAY;AAAA,EAC3C;AACA,MAAI,OAAO,YAAY,UAAa,CAAC,SAAS,SAAS,OAAO,OAAiB,GAAG;AAChF,WAAO,EAAE,IAAI,OAAO,SAAS,YAAY;AAAA,EAC3C;AACA,QAAM,OAAO,UAAU,OAAO,IAAI;AAClC,MAAI,SAAS,IAAI;AACf,WAAO,EAAE,IAAI,OAAO,SAAS,QAAQ;AAAA,EACvC;AACA,MAAI,CAAC,GAAG,IAAI,EAAE,SAAS,oBAAoB;AACzC,WAAO,EAAE,IAAI,OAAO,SAAS,WAAW;AAAA,EAC1C;AACA,QAAM,aAAa,aAAa,OAAO,UAAU;AACjD,QAAM,QAAQ,QAAQ,OAAO,KAAK;AAClC,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,QAAQ;AAAA,MACN;AAAA,MACA,GAAI,OAAO,YAAY,SAAY,EAAE,SAAS,OAAO,QAAqB,IAAI,CAAC;AAAA,MAC/E,GAAI,eAAe,SAAY,EAAE,WAAW,IAAI,CAAC;AAAA,MACjD,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;AAAA,IACzC;AAAA,EACF;AACF;AAQO,SAAS,mBACd,KACA,SACwE;AACxE,QAAM,OAAO,iBAAiB,GAAG;AACjC,MAAI,CAAC,KAAK,IAAI;AACZ,WAAO;AAAA,EACT;AACA,MAAI,KAAK,OAAO,YAAY,UAAa,KAAK,OAAO,YAAY,QAAQ,MAAM,UAAU;AACvF,WAAO,EAAE,IAAI,OAAO,SAAS,oBAAoB;AAAA,EACnD;AACA,SAAO;AACT;AAOO,SAAS,YACd,KACA,SACwE;AACxE,QAAM,OAAO,iBAAiB,GAAG;AACjC,MAAI,CAAC,KAAK,IAAI;AACZ,WAAO;AAAA,EACT;AACA,MAAI,kBAAkB,QAAQ,OAAO,KAAK,OAAO,IAAI,GAAG;AACtD,WAAO,EAAE,IAAI,OAAO,SAAS,iBAAiB;AAAA,EAChD;AACA,SAAO;AACT;AASA,SAAS,KAAK,MAAwB;AACpC,QAAMA,UAAS,KACZ,UAAU,MAAM,EAChB,QAAQ,WAAW,EAAE,EACrB,YAAY,EACZ,QAAQ,oBAAoB,GAAG,EAC/B,KAAK;AACR,SAAOA,YAAW,KAAK,CAAC,IAAIA,QAAO,MAAM,GAAG;AAC9C;AAGA,SAAS,cAAc,UAA6BC,SAAoC;AACtF,MAAIA,QAAO,WAAW,KAAKA,QAAO,SAAS,SAAS,QAAQ;AAC1D,WAAO;AAAA,EACT;AACA,WAAS,QAAQ,GAAG,QAAQA,QAAO,UAAU,SAAS,QAAQ,SAAS,GAAG;AACxE,QAAI,QAAQ;AACZ,aAAS,SAAS,GAAG,SAASA,QAAO,QAAQ,UAAU,GAAG;AACxD,UAAI,SAAS,QAAQ,MAAM,MAAMA,QAAO,MAAM,GAAG;AAC/C,gBAAQ;AACR;AAAA,MACF;AAAA,IACF;AACA,QAAI,OAAO;AACT,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAOA,IAAM,UAAU,CAAC,UACf,MAAM,WAAW,KAAK,CAAC,GAAI,MAAM,CAAC,CAAY,EAAE,SAAS;AAM3D,SAAS,WAAW,SAAiB,UAAuD;AAC1F,QAAM,SAAS,IAAI,QAAQ;AAC3B,QAAM,KAAK,QAAQ,QAAQ,MAAM;AACjC,MAAI,OAAO,IAAI;AACb,WAAO,CAAC;AAAA,EACV;AACA,QAAM,QAAQ,CAAC,SAAyB,KAAK,QAAQ,YAAY,GAAG;AACpE,QAAM,OAAO,KAAK,MAAM,QAAQ,MAAM,GAAG,EAAE,CAAC,CAAC;AAC7C,QAAM,QAAQ,KAAK,MAAM,QAAQ,MAAM,KAAK,OAAO,MAAM,CAAC,CAAC;AAC3D,QAAM,SAAS,KAAK,KAAK,SAAS,CAAC;AACnC,QAAM,QAAQ,MAAM,CAAC;AACrB,SAAO;AAAA,IACL,GAAI,WAAW,SAAY,EAAE,OAAO,IAAI,CAAC;AAAA,IACzC,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;AAAA,EACzC;AACF;AAeO,SAAS,kBAAkB,OAAoB,MAAuB;AAC3E,QAAM,QAAQ,KAAK,IAAI;AACvB,QAAM,UAAU,CAAC,QAAgB,SAAkB,aAA+B;AAChF,UAAMA,UAAS,KAAK,MAAM;AAC1B,QAAIA,QAAO,WAAW,GAAG;AACvB,aAAO;AAAA,IACT;AACA,QAAI,CAAC,QAAQA,OAAM,KAAK,YAAY,UAAa,aAAa,QAAW;AACvE,aAAO,cAAc,OAAOA,OAAM;AAAA,IACpC;AACA,UAAM,EAAE,QAAQ,MAAM,IAAI,WAAW,SAAS,QAAQ;AACtD,QAAI,WAAW,UAAa,UAAU,QAAW;AAC/C,aAAO,cAAc,OAAOA,OAAM;AAAA,IACpC;AACA,WACG,WAAW,UAAa,cAAc,OAAO,CAAC,QAAQ,GAAGA,OAAM,CAAC,KAChE,UAAU,UAAa,cAAc,OAAO,CAAC,GAAGA,SAAQ,KAAK,CAAC;AAAA,EAEnE;AACA,UAAQ,MAAM,cAAc;AAAA,IAC1B,KAAK;AACH,aAAO,MAAM,QAAQ,KAAK,CAAC,WAAW,OAAO,YAAY,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,IACvF,KAAK;AACH,aAAO,MAAM,OAAO;AAAA,QAAK,CAAC,WACvB,MAAM,YAAY,CAAC,GAAG,KAAK,CAAC,WAAW,QAAQ,QAAQ,MAAM,SAAS,MAAM,QAAQ,CAAC;AAAA,MACxF;AAAA,IACF,KAAK;AACH,aAAO,MAAM,KAAK;AAAA,QAChB,CAAC,QAAQ,IAAI,WAAW,QAAQ,QAAQ,IAAI,QAAQ,MAAM,SAAS,IAAI,QAAQ;AAAA,MACjF;AAAA,IACF,KAAK;AAEH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;;;AC9nBO,IAAM,6BAA6B;AAGnC,IAAM,8BAA8B;AAG3C,IAAM,iBAAiB;AAahB,SAAS,yBAAyB,OAKL;AAClC,QAAM,EAAE,MAAM,UAAU,cAAc,IAAI;AAC1C,MACE,CAAC,WAAW,KAAK,MAAM,kBAAkB,KACzC,CAAC,mBAAmB,MAAM,kBAAkB,KAC3C,KAAgC,aAAa,MAC9C;AACA,WAAO;AAAA,EACT;AACA,QAAM,OAAO,UAAU,SAAS,qBAAqB,SAAS,OAAO;AACrE,MAAI,OAAO,SAAS,YAAY,KAAK,KAAK,MAAM,IAAI;AAClD,WAAO;AAAA,EACT;AACA,QAAM,OAAO;AACb,QAAM,YAAY,WAAW,IAAI;AACjC,QAAM,WAAW,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW;AACrE,QAAM,WAAW,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW,OAAO;AAC5E,QAAM,WAAW,MAAM,QAAQ,KAAK,QAAQ,QAAQ,IAAI,KAAK,OAAO,WAAW;AAC/E,QAAM,SACJ,aAAa,QAAQ,SAAS,WAAW,IACrC,OACA,SAAS,IAAI,CAAC,eAAe;AAAA,IAC3B,MAAM,UAAU;AAAA,IAChB,GAAI,OAAO,UAAU,gBAAgB,WACjC,EAAE,aAAa,UAAU,YAAY,IACrC,CAAC;AAAA,IACL,QAAQ,UAAU;AAAA,EACpB,EAAE;AACR,QAAM,mBAAmB,CAAC,GAAI,MAAM,oBAAoB,CAAC,CAAE;AAC3D,SAAO;AAAA,IACL,SAAS;AAAA,IACT,OAAO;AAAA,MACL,cAAc;AAAA,MACd,YAAY,KAAK;AAAA,MACjB,OAAO,KAAK;AAAA,MACZ,GAAI,OAAO,KAAK,WAAW,WAAW,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,MACjE,QAAQ,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS;AAAA,MACxD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,kBAAkB,aAAa,YAAY,aAAa;AAAA,MACxD;AAAA,MACA,GAAI,OAAO,KAAK,mBAAmB,WAAW,EAAE,gBAAgB,KAAK,eAAe,IAAI,CAAC;AAAA,IAC3F;AAAA,IACA,aAAa,iBAAiB,SAAS;AAAA,IACvC;AAAA,IACA,GAAI,kBAAkB,SAAY,EAAE,cAAc,IAAI,CAAC;AAAA,EACzD;AACF;AAKA,IAAM,aAA+C;AAAA,EACnD,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AAAA,EACL,UAAK;AACP;AASA,SAAS,OAAO,MAAiD;AAC/D,MAAI,QAAQ;AACZ,QAAM,OAAiB,CAAC;AACxB,MAAI,QAAQ;AACZ,WAAS,KAAK,GAAG,KAAK,KAAK,QAAQ,MAAM,GAAG;AAC1C,UAAM,OAAO,KAAK,EAAE;AACpB,QAAI,MAAM,KAAK,IAAI,GAAG;AACpB,UAAI,CAAC,OAAO;AACV,iBAAS;AACT,aAAK,KAAK,EAAE;AACZ,gBAAQ;AAAA,MACV;AACA;AAAA,IACF;AACA,YAAQ;AACR,aAAS,WAAW,IAAI,KAAK;AAC7B,SAAK,KAAK,EAAE;AAAA,EACd;AACA,SAAO,EAAE,OAAO,KAAK;AACvB;AAGA,IAAM,SAAS,CAAC,UAA0B,OAAO,KAAK,EAAE,MAAM,KAAK;AASnE,SAAS,OACP,OACA,OACA,SACA,OACuC;AACvC,QAAM,SAAS,OAAO,KAAK;AAC3B,MAAI,WAAW,IAAI;AACjB,WAAO;AAAA,EACT;AACA,MAAI,YAAY,QAAW;AACzB,UAAM,EAAE,OAAO,IAAI,IAAI;AACvB,QACE,CAAC,OAAO,cAAc,KAAK,KAC3B,CAAC,OAAO,cAAc,GAAG,KACzB,QAAQ,KACR,MAAM,MAAM,KAAK,UACjB,SAAS,KACT;AACA,aAAO;AAAA,IACT;AACA,UAAM,OAAO,MAAM,KAAK,MAAM,OAAO,GAAG;AACxC,QAAI,OAAO,IAAI,MAAM,QAAQ;AAC3B,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,KAAK,SAAS,KAAK,UAAU,EAAE;AAC5C,UAAM,QAAQ,KAAK,SAAS,KAAK,QAAQ,EAAE;AAC3C,WAAO,EAAE,OAAO,QAAQ,MAAM,KAAK,MAAM,MAAM;AAAA,EACjD;AACA,MAAI,QAA+C;AACnD,WAAS,KAAK,MAAM,MAAM,QAAQ,MAAM,GAAG,OAAO,IAAI,KAAK,MAAM,MAAM,QAAQ,QAAQ,KAAK,CAAC,GAAG;AAC9F,UAAM,QAAQ,MAAM,KAAK,EAAE;AAC3B,UAAM,MAAO,MAAM,KAAK,KAAK,OAAO,SAAS,CAAC,IAAe;AAC7D,UAAM,QAAQ,EAAE,OAAO,IAAI;AAC3B,cAAU;AACV,UAAM,MAAM,GAAG,KAAK,IAAI,GAAG;AAC3B,QAAI,CAAC,MAAM,IAAI,GAAG,GAAG;AACnB,YAAM,IAAI,GAAG;AACb,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,UACP,OACA,KAC6E;AAC7E,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO,EAAE,IAAI,MAAM,OAAO,OAAU;AAAA,EACtC;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,EAAE,IAAI,OAAO,SAAS,YAAY;AAAA,EAC3C;AACA,QAAM,UAAU,UAAU,KAAK;AAC/B,MAAI,CAAC,GAAG,OAAO,EAAE,SAAS,KAAK;AAC7B,WAAO,EAAE,IAAI,OAAO,SAAS,WAAW;AAAA,EAC1C;AACA,SAAO,EAAE,IAAI,MAAM,OAAO,YAAY,KAAK,SAAY,QAAQ;AACjE;AAEA,IAAM,WAAW,CAAC,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAGrE,SAAS,iBACP,KACA,MACmF;AACnF,MAAI,QAAQ,UAAa,QAAQ,MAAM;AACrC,WAAO,EAAE,IAAI,MAAM,aAAa,CAAC,EAAE;AAAA,EACrC;AACA,MAAI,CAAC,MAAM,QAAQ,GAAG,GAAG;AACvB,WAAO,EAAE,IAAI,OAAO,SAAS,YAAY;AAAA,EAC3C;AACA,MAAI,IAAI,SAAS,4BAA4B;AAC3C,WAAO,EAAE,IAAI,OAAO,SAAS,WAAW;AAAA,EAC1C;AACA,QAAM,QAAQ,EAAE,MAAM,GAAG,OAAO,IAAI,EAAE;AACtC,QAAM,QAAQ,oBAAI,IAAY;AAC9B,QAAM,cAAkC,CAAC;AACzC,aAAW,SAAS,KAAK;AACvB,QAAI,CAAC,SAAS,KAAK,KAAK,OAAO,MAAM,aAAa,UAAU;AAC1D,aAAO,EAAE,IAAI,OAAO,SAAS,YAAY;AAAA,IAC3C;AACA,QAAI,OAAO,MAAM,cAAc,UAAU;AACvC,aAAO,EAAE,IAAI,OAAO,SAAS,YAAY;AAAA,IAC3C;AACA,QACE,CAAC,GAAG,MAAM,QAAQ,EAAE,SAAS,+BAC7B,CAAC,GAAG,MAAM,SAAS,EAAE,SAAS,6BAC9B;AACA,aAAO,EAAE,IAAI,OAAO,SAAS,WAAW;AAAA,IAC1C;AAEA,QAAI,OAAO,MAAM,QAAQ,MAAM,OAAO,MAAM,SAAS,GAAG;AACtD,aAAO,EAAE,IAAI,OAAO,SAAS,YAAY;AAAA,IAC3C;AACA,UAAM,cAAc,UAAU,MAAM,aAAa,2BAA2B;AAC5E,UAAM,WAAW,UAAU,MAAM,UAAU,cAAc;AACzD,QAAI,CAAC,YAAY,IAAI;AACnB,aAAO;AAAA,IACT;AACA,QAAI,CAAC,SAAS,IAAI;AAChB,aAAO;AAAA,IACT;AACA,QAAI;AACJ,QAAI,MAAM,UAAU,UAAa,MAAM,UAAU,MAAM;AACrD,UAAI,CAAC,SAAS,MAAM,KAAK,GAAG;AAC1B,eAAO,EAAE,IAAI,OAAO,SAAS,YAAY;AAAA,MAC3C;AACA,gBAAU,EAAE,OAAO,MAAM,MAAM,OAAiB,KAAK,MAAM,MAAM,IAAc;AAAA,IACjF;AACA,UAAM,KAAK,OAAO,OAAO,MAAM,UAAU,SAAS,KAAK;AACvD,QAAI,OAAO,MAAM;AACf,aAAO,EAAE,IAAI,OAAO,SAAS,mBAAmB;AAAA,IAClD;AACA,gBAAY,KAAK;AAAA,MACf,UAAU,KAAK,MAAM,GAAG,OAAO,GAAG,GAAG;AAAA,MACrC,WAAW,UAAU,MAAM,SAAS;AAAA,MACpC,GAAI,YAAY,UAAU,SAAY,EAAE,aAAa,YAAY,MAAM,IAAI,CAAC;AAAA,MAC5E,GAAI,SAAS,UAAU,SAAY,EAAE,UAAU,SAAS,MAAM,IAAI,CAAC;AAAA,MACnE,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,SAAO,EAAE,IAAI,MAAM,YAAY;AACjC;AAGA,SAAS,cAAcC,QAAgB,UAA4B;AACjE,MAAIA,WAAU,QAAW;AACvB,WAAO,aAAa,UAAc,OAAO,aAAa,YAAY,WAAW;AAAA,EAC/E;AACA,QAAM,MAAM,YAAY;AACxB,SACE,OAAOA,WAAU,YACjB,OAAO,QAAQ,YACf,OAAO,SAASA,MAAK,KACrB,OAAO,SAAS,GAAG,KACnB,MAAM,KACNA,UAAS,KACTA,SAAQ,OAAO,MAAM;AAEzB;AAGA,SAAS,cACP,KACA,QAC8E;AAC9E,MAAI,QAAQ,UAAa,QAAQ,MAAM;AACrC,WAAO,EAAE,IAAI,MAAM,UAAU,CAAC,EAAE;AAAA,EAClC;AACA,MAAI,CAAC,MAAM,QAAQ,GAAG,GAAG;AACvB,WAAO,EAAE,IAAI,OAAO,SAAS,YAAY;AAAA,EAC3C;AACA,MAAI,IAAI,WAAW,GAAG;AACpB,WAAO,EAAE,IAAI,MAAM,UAAU,CAAC,EAAE;AAAA,EAClC;AAEA,MAAI,WAAW,MAAM;AACnB,WAAO,EAAE,IAAI,OAAO,SAAS,YAAY;AAAA,EAC3C;AACA,QAAM,UAAU,IAAI,IAAI,OAAO,IAAI,CAAC,cAAc,CAAC,UAAU,MAAM,UAAU,MAAM,CAAC,CAAC;AACrF,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,WAA6B,CAAC;AACpC,aAAW,SAAS,KAAK;AACvB,QAAI,CAAC,SAAS,KAAK,KAAK,OAAO,MAAM,SAAS,UAAU;AACtD,aAAO,EAAE,IAAI,OAAO,SAAS,YAAY;AAAA,IAC3C;AACA,UAAM,SAAS,QAAQ,IAAI,MAAM,IAAI;AACrC,QAAI,WAAW,UAAa,KAAK,IAAI,MAAM,IAAI,GAAG;AAChD,aAAO,EAAE,IAAI,OAAO,SAAS,YAAY;AAAA,IAC3C;AACA,SAAK,IAAI,MAAM,IAAI;AACnB,QAAI,CAAC,cAAc,MAAM,OAAO,MAAM,QAAQ,GAAG;AAC/C,aAAO,EAAE,IAAI,OAAO,SAAS,YAAY;AAAA,IAC3C;AACA,QAAI,MAAM,kBAAkB,UAAa,OAAO,MAAM,kBAAkB,WAAW;AACjF,aAAO,EAAE,IAAI,OAAO,SAAS,YAAY;AAAA,IAC3C;AACA,UAAM,OAAO,UAAU,MAAM,MAAM,cAAc;AACjD,UAAM,UAAU,UAAU,MAAM,SAAS,2BAA2B;AACpE,QAAI,CAAC,KAAK,IAAI;AACZ,aAAO;AAAA,IACT;AACA,QAAI,CAAC,QAAQ,IAAI;AACf,aAAO;AAAA,IACT;AACA,aAAS,KAAK;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,GAAI,OAAO,MAAM,UAAU,WAAW,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,MAChE,GAAI,OAAO,MAAM,aAAa,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;AAAA,MACzE,GAAI,KAAK,UAAU,SAAY,EAAE,MAAM,KAAK,MAAM,IAAI,CAAC;AAAA,MACvD,GAAI,QAAQ,UAAU,SAAY,EAAE,SAAS,QAAQ,MAAM,IAAI,CAAC;AAAA,MAChE,GAAI,MAAM,kBAAkB,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;AAAA,MAC9D;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO,EAAE,IAAI,MAAM,SAAS;AAC9B;AA0BO,SAAS,uBACd,KACA,SAC+E;AAC/E,QAAM,OAAO,iBAAiB,GAAG;AACjC,MAAI,CAAC,KAAK,IAAI;AACZ,WAAO;AAAA,EACT;AACA,QAAM,SAAS;AACf,QAAM,cAAc,iBAAiB,OAAO,aAAa,QAAQ,MAAM,IAAI;AAC3E,MAAI,CAAC,YAAY,IAAI;AACnB,WAAO;AAAA,EACT;AACA,QAAM,SAAS,QAAQ,MAAM;AAC7B,QAAM,WAAW,cAAc,OAAO,UAAU,MAAM;AACtD,MAAI,CAAC,SAAS,IAAI;AAChB,WAAO;AAAA,EACT;AACA,MAAI,kBAAiC;AACrC,QAAM,SAAS,IAAI,IAAI,SAAS,SAAS,IAAI,CAAC,cAAc,UAAU,IAAI,CAAC;AAC3E,MAAI,QAAQ,MAAM,CAAC,cAAc,OAAO,IAAI,UAAU,IAAI,CAAC,GAAG;AAC5D,UAAM,QAAQ,gBAAgB,SAAS,QAAQ;AAC/C,sBAAkB,gBAAgB,QAAQ,OAAO,MAAM;AAAA,EACzD;AACA,QAAM,EAAE,MAAM,YAAY,MAAM,IAAI,KAAK;AACzC,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,UAAU;AAAA,MACR;AAAA,MACA,aAAa,YAAY;AAAA,MACzB,UAAU,SAAS;AAAA,MACnB;AAAA,MACA,GAAI,eAAe,SAAY,EAAE,WAAW,IAAI,CAAC;AAAA,MACjD,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;AAAA,IACzC;AAAA,EACF;AACF;","names":["folded","needle","score"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ActivitySchemaError,
|
|
3
3
|
getActivityTypeDescriptor
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-FBKTION6.js";
|
|
5
5
|
|
|
6
6
|
// src/xapi/validators.ts
|
|
7
7
|
import { z } from "zod/v4";
|
|
@@ -269,4 +269,4 @@ export {
|
|
|
269
269
|
xAPIBuilder,
|
|
270
270
|
xapiDefinitionFor
|
|
271
271
|
};
|
|
272
|
-
//# sourceMappingURL=chunk-
|
|
272
|
+
//# sourceMappingURL=chunk-SJVRUPJT.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
|
|
4
|
+
var _chunkDSDXL3JOcjs = require('./chunk-DSDXL3JO.cjs');
|
|
5
5
|
|
|
6
6
|
// src/xapi/validators.ts
|
|
7
7
|
var _v4 = require('zod/v4');
|
|
@@ -86,7 +86,7 @@ function validateXAPIStatement(statement) {
|
|
|
86
86
|
console.warn("[learning-kit] Invalid xAPI statement (sent anyway):", errors);
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
|
-
throw new (0,
|
|
89
|
+
throw new (0, _chunkDSDXL3JOcjs.ActivitySchemaError)("xAPIStatement", errors);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
// src/xapi/verbs.ts
|
|
@@ -249,7 +249,7 @@ var xAPIBuilder = {
|
|
|
249
249
|
}
|
|
250
250
|
};
|
|
251
251
|
function xapiDefinitionFor(data) {
|
|
252
|
-
const descriptor =
|
|
252
|
+
const descriptor = _chunkDSDXL3JOcjs.getActivityTypeDescriptor.call(void 0, data.type);
|
|
253
253
|
const interop = _optionalChain([descriptor, 'optionalAccess', _3 => _3.interop]);
|
|
254
254
|
if (interop === void 0) {
|
|
255
255
|
return {};
|
|
@@ -269,4 +269,4 @@ function xapiDefinitionFor(data) {
|
|
|
269
269
|
|
|
270
270
|
|
|
271
271
|
exports.validateXAPIStatement = validateXAPIStatement; exports.XAPIVerb = XAPIVerb; exports.XAPI_VERB_DISPLAY = XAPI_VERB_DISPLAY; exports.xAPIBuilder = xAPIBuilder; exports.xapiDefinitionFor = xapiDefinitionFor;
|
|
272
|
-
//# sourceMappingURL=chunk-
|
|
272
|
+
//# sourceMappingURL=chunk-TJJFKKH2.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/learning-kit/learning-kit/packages/lk-core/dist/chunk-EOFWSGLU.cjs","../src/xapi/validators.ts","../src/xapi/verbs.ts","../src/xapi/builder.ts"],"names":[],"mappings":"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ACF,EAAA;AAEA,EAAA;AACE,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACI,MAAA;AACL,IAAA;AACH,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA,EAAA;AACE,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACI,MAAA;AACL,IAAA;AACH,EAAA;AAEA,EAAA;AACE,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACI,MAAA;AACL,IAAA;AACH,EAAA;AACF;AAgBgB;AACR,EAAA;AACA,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACM,EAAA;AACC,EAAA;AACD,IAAA;AACA,IAAA;AAGA,IAAA;AACN,EAAA;AACF;AHLU;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/learning-kit/learning-kit/packages/lk-core/dist/chunk-EOFWSGLU.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/**\n * The one global this module reads by name. Declared here, not globally: this\n * package compiles without Node's types on purpose, and nothing else in it may\n * come to rely on `process`.\n */\ndeclare const process: { env: { NODE_ENV?: string } };\n\n/**\n * Whether this is a production build.\n *\n * **The literal `process.env.NODE_ENV`, spelled exactly so.** It is the one\n * expression bundlers replace at build time, and a browser has no `process` to\n * read at run time. The reading this replaces,\n * `globalThis.process?.env?.NODE_ENV`, is not that expression, so no bundler\n * touched it, and in a browser it found no `process` and answered \"not\n * production\" in every production app — so a malformed statement threw there\n * instead of being logged and sent. Tests running in Node never saw it,\n * because Node has a `process`.\n *\n * This package's own build must leave the literal in its output, for the\n * consumer's bundler to replace. The `try` covers code that reaches a runtime\n * unbundled, with no `process` at all: that is not production, as it always\n * was.\n */\nfunction isProduction(): boolean {\n try {\n return process.env.NODE_ENV === 'production';\n } catch {\n return false;\n }\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-TJJFKKH2.cjs","../src/xapi/validators.ts","../src/xapi/verbs.ts","../src/xapi/builder.ts"],"names":[],"mappings":"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ACF,EAAA;AAEA,EAAA;AACE,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACI,MAAA;AACL,IAAA;AACH,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA,EAAA;AACE,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACI,MAAA;AACL,IAAA;AACH,EAAA;AAEA,EAAA;AACE,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACI,MAAA;AACL,IAAA;AACH,EAAA;AACF;AAgBgB;AACR,EAAA;AACA,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACM,EAAA;AACC,EAAA;AACD,IAAA;AACA,IAAA;AAGA,IAAA;AACN,EAAA;AACF;AHLU;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/learning-kit/learning-kit/packages/lk-core/dist/chunk-TJJFKKH2.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/**\n * The one global this module reads by name. Declared here, not globally: this\n * package compiles without Node's types on purpose, and nothing else in it may\n * come to rely on `process`.\n */\ndeclare const process: { env: { NODE_ENV?: string } };\n\n/**\n * Whether this is a production build.\n *\n * **The literal `process.env.NODE_ENV`, spelled exactly so.** It is the one\n * expression bundlers replace at build time, and a browser has no `process` to\n * read at run time. The reading this replaces,\n * `globalThis.process?.env?.NODE_ENV`, is not that expression, so no bundler\n * touched it, and in a browser it found no `process` and answered \"not\n * production\" in every production app — so a malformed statement threw there\n * instead of being logged and sent. Tests running in Node never saw it,\n * because Node has a `process`.\n *\n * This package's own build must leave the literal in its output, for the\n * consumer's bundler to replace. The `try` covers code that reaches a runtime\n * unbundled, with no `process` at all: that is not production, as it always\n * was.\n */\nfunction isProduction(): boolean {\n try {\n return process.env.NODE_ENV === 'production';\n } catch {\n return false;\n }\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"]}
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
var _chunkDSDXL3JOcjs = require('./chunk-DSDXL3JO.cjs');
|
|
18
19
|
|
|
19
20
|
// src/is-redacted.ts
|
|
20
21
|
function isRedacted(data) {
|
|
@@ -450,12 +451,12 @@ function composeAssessmentScore(sections, policy) {
|
|
|
450
451
|
function diffDictationChars(reference, attempt) {
|
|
451
452
|
const referencePoints = Array.from(reference);
|
|
452
453
|
const attemptPoints = Array.from(attempt);
|
|
453
|
-
return
|
|
454
|
+
return _chunkDSDXL3JOcjs.alignSequences.call(void 0, referencePoints, attemptPoints).map(
|
|
454
455
|
({ step, referenceIndex, attemptIndex }) => {
|
|
455
|
-
if (step ===
|
|
456
|
+
if (step === _chunkDSDXL3JOcjs.STEP.missing) {
|
|
456
457
|
return { op: "missing", reference: referencePoints[referenceIndex], attempt: "" };
|
|
457
458
|
}
|
|
458
|
-
if (step ===
|
|
459
|
+
if (step === _chunkDSDXL3JOcjs.STEP.extra) {
|
|
459
460
|
return { op: "extra", reference: "", attempt: attemptPoints[attemptIndex] };
|
|
460
461
|
}
|
|
461
462
|
const referencePoint = referencePoints[referenceIndex];
|
|
@@ -477,14 +478,14 @@ function heardTokens(words) {
|
|
|
477
478
|
const tokens = [];
|
|
478
479
|
let length = 0;
|
|
479
480
|
for (const [wordIndex, word] of words.entries()) {
|
|
480
|
-
const normalized =
|
|
481
|
+
const normalized = _chunkDSDXL3JOcjs.normalizeDictationText.call(void 0, word.text);
|
|
481
482
|
if (normalized === "") {
|
|
482
483
|
continue;
|
|
483
484
|
}
|
|
484
485
|
const inserted = word.error === "insertion";
|
|
485
486
|
for (const text of normalized.split(" ")) {
|
|
486
|
-
length +=
|
|
487
|
-
if (!inserted && length <=
|
|
487
|
+
length += _chunkDSDXL3JOcjs.codePointLength.call(void 0, text) + (length === 0 ? 0 : 1);
|
|
488
|
+
if (!inserted && length <= _chunkDSDXL3JOcjs.SPEECH_ASSESSMENT_MAX_TEXT_LENGTH) {
|
|
488
489
|
tokens.push({ text, wordIndex });
|
|
489
490
|
}
|
|
490
491
|
}
|
|
@@ -497,28 +498,28 @@ var Score = _v4.z.number().min(0).max(100);
|
|
|
497
498
|
var Confidence = _v4.z.number().min(0).max(1);
|
|
498
499
|
var Milliseconds = _v4.z.number().min(0);
|
|
499
500
|
var PhonemeCandidateSchema = _v4.z.strictObject({
|
|
500
|
-
symbol: _v4.z.string().min(1)
|
|
501
|
+
symbol: _chunkDSDXL3JOcjs.maxUnits.call(void 0, _v4.z.string().min(1), 16),
|
|
501
502
|
score: Score
|
|
502
503
|
});
|
|
503
504
|
var PhonemeSchema = _v4.z.strictObject({
|
|
504
|
-
symbol: _v4.z.string().min(1)
|
|
505
|
+
symbol: _chunkDSDXL3JOcjs.maxUnits.call(void 0, _v4.z.string().min(1), 16).optional(),
|
|
505
506
|
accuracy: Score.optional(),
|
|
506
507
|
startMs: Milliseconds.optional(),
|
|
507
508
|
durationMs: Milliseconds.optional(),
|
|
508
509
|
heardAs: _v4.z.array(PhonemeCandidateSchema).max(10).optional()
|
|
509
510
|
});
|
|
510
511
|
var SyllableSchema = _v4.z.strictObject({
|
|
511
|
-
text: _v4.z.string().min(1)
|
|
512
|
-
grapheme: _v4.z.string()
|
|
512
|
+
text: _chunkDSDXL3JOcjs.maxUnits.call(void 0, _v4.z.string().min(1), 64),
|
|
513
|
+
grapheme: _chunkDSDXL3JOcjs.maxUnits.call(void 0, _v4.z.string(), 64).optional(),
|
|
513
514
|
accuracy: Score.optional(),
|
|
514
515
|
startMs: Milliseconds.optional(),
|
|
515
516
|
durationMs: Milliseconds.optional()
|
|
516
517
|
});
|
|
517
518
|
var WordSchema = _v4.z.strictObject({
|
|
518
|
-
text: _v4.z.string().min(1)
|
|
519
|
+
text: _chunkDSDXL3JOcjs.maxUnits.call(void 0, _v4.z.string().min(1), 200),
|
|
519
520
|
accuracy: Score.optional(),
|
|
520
521
|
error: _v4.z.enum(["none", "mispronunciation", "omission", "insertion"]),
|
|
521
|
-
vendorError: _v4.z.string()
|
|
522
|
+
vendorError: _chunkDSDXL3JOcjs.maxUnits.call(void 0, _v4.z.string(), 64).optional(),
|
|
522
523
|
startMs: Milliseconds.optional(),
|
|
523
524
|
durationMs: Milliseconds.optional(),
|
|
524
525
|
syllables: _v4.z.array(SyllableSchema).max(50).optional(),
|
|
@@ -527,17 +528,17 @@ var WordSchema = _v4.z.strictObject({
|
|
|
527
528
|
});
|
|
528
529
|
var AssessorSchema = _v4.z.strictObject({
|
|
529
530
|
kind: _v4.z.enum(["auto", "ai", "human"]),
|
|
530
|
-
id: _v4.z.string()
|
|
531
|
-
model: _v4.z.string()
|
|
532
|
-
promptHash: _v4.z.string()
|
|
531
|
+
id: _chunkDSDXL3JOcjs.maxUnits.call(void 0, _v4.z.string(), 256).optional(),
|
|
532
|
+
model: _chunkDSDXL3JOcjs.maxUnits.call(void 0, _v4.z.string(), 256).optional(),
|
|
533
|
+
promptHash: _chunkDSDXL3JOcjs.maxUnits.call(void 0, _v4.z.string(), 256).optional()
|
|
533
534
|
});
|
|
534
535
|
var SpeechAssessmentSchema = _v4.z.strictObject({
|
|
535
536
|
assessmentVersion: _v4.z.literal("1.0"),
|
|
536
537
|
status: _v4.z.enum(["assessed", "no_speech"]),
|
|
537
538
|
task: _v4.z.enum(["scripted", "unscripted"]),
|
|
538
|
-
locale: _v4.z.string().regex(
|
|
539
|
-
referenceText: _v4.z.string()
|
|
540
|
-
recordingKey: _v4.z.string().min(1)
|
|
539
|
+
locale: _v4.z.string().regex(_chunkDSDXL3JOcjs.CANONICAL_LOCALE_RE),
|
|
540
|
+
referenceText: _chunkDSDXL3JOcjs.maxUnits.call(void 0, _v4.z.string(), 8e3).optional(),
|
|
541
|
+
recordingKey: _chunkDSDXL3JOcjs.maxUnits.call(void 0, _v4.z.string().min(1), 1024).optional(),
|
|
541
542
|
assessor: AssessorSchema,
|
|
542
543
|
scale: _v4.z.literal(100),
|
|
543
544
|
scores: _v4.z.strictObject({
|
|
@@ -547,10 +548,10 @@ var SpeechAssessmentSchema = _v4.z.strictObject({
|
|
|
547
548
|
prosody: Score.optional(),
|
|
548
549
|
overall: Score.optional()
|
|
549
550
|
}),
|
|
550
|
-
recognizedText: _v4.z.string()
|
|
551
|
+
recognizedText: _chunkDSDXL3JOcjs.maxUnits.call(void 0, _v4.z.string(), 8e3).optional(),
|
|
551
552
|
miscue: _v4.z.enum(["assessor", "none"]),
|
|
552
553
|
phonemeAlphabet: _v4.z.enum(["ipa", "sapi"]).optional(),
|
|
553
|
-
words: _v4.z.array(WordSchema).max(
|
|
554
|
+
words: _v4.z.array(WordSchema).max(_chunkDSDXL3JOcjs.SPEECH_ASSESSMENT_MAX_WORDS),
|
|
554
555
|
prosody: _v4.z.strictObject({ monotoneConfidence: Confidence.optional() }).optional(),
|
|
555
556
|
signal: _v4.z.strictObject({ snrDb: _v4.z.number().optional() }).optional()
|
|
556
557
|
});
|
|
@@ -572,21 +573,21 @@ function rawTextLength(words) {
|
|
|
572
573
|
}
|
|
573
574
|
function totalTextErrors(words) {
|
|
574
575
|
const raw = rawTextLength(words);
|
|
575
|
-
if (raw >
|
|
576
|
+
if (raw > _chunkDSDXL3JOcjs.SPEECH_ASSESSMENT_MAX_TEXT_LENGTH) {
|
|
576
577
|
return [
|
|
577
578
|
{
|
|
578
579
|
path: ["words"],
|
|
579
|
-
message: `The words of an assessment carry ${raw} characters between them, and at most ${
|
|
580
|
+
message: `The words of an assessment carry ${raw} characters between them, and at most ${_chunkDSDXL3JOcjs.SPEECH_ASSESSMENT_MAX_TEXT_LENGTH} can be aligned against the text of an item.`,
|
|
580
581
|
code: "too_big"
|
|
581
582
|
}
|
|
582
583
|
];
|
|
583
584
|
}
|
|
584
585
|
const normalized = heardTokens(words).length;
|
|
585
|
-
if (normalized >
|
|
586
|
+
if (normalized > _chunkDSDXL3JOcjs.SPEECH_ASSESSMENT_MAX_TEXT_LENGTH) {
|
|
586
587
|
return [
|
|
587
588
|
{
|
|
588
589
|
path: ["words"],
|
|
589
|
-
message: `The words of an assessment spell ${normalized} characters between them once they are normalised, and at most ${
|
|
590
|
+
message: `The words of an assessment spell ${normalized} characters between them once they are normalised, and at most ${_chunkDSDXL3JOcjs.SPEECH_ASSESSMENT_MAX_TEXT_LENGTH} can be aligned against the text of an item. One character can stand for several.`,
|
|
590
591
|
code: "too_big"
|
|
591
592
|
}
|
|
592
593
|
];
|
|
@@ -656,16 +657,16 @@ function alignReadAloud(data, assessment) {
|
|
|
656
657
|
return alignValidated(data, checked.data);
|
|
657
658
|
}
|
|
658
659
|
function alignValidated(data, assessment) {
|
|
659
|
-
const reference =
|
|
660
|
+
const reference = _chunkDSDXL3JOcjs.dictationReferenceWords.call(void 0, {
|
|
660
661
|
transcript: data.referenceText
|
|
661
662
|
})[0];
|
|
662
663
|
const heard = heardTokens(assessment.words).tokens;
|
|
663
664
|
const entries = [];
|
|
664
|
-
for (const { step, referenceIndex, attemptIndex } of
|
|
665
|
+
for (const { step, referenceIndex, attemptIndex } of _chunkDSDXL3JOcjs.alignSequences.call(void 0,
|
|
665
666
|
reference.map((word) => word.word),
|
|
666
667
|
heard.map((token) => token.text)
|
|
667
668
|
)) {
|
|
668
|
-
if (step ===
|
|
669
|
+
if (step === _chunkDSDXL3JOcjs.STEP.extra) {
|
|
669
670
|
const token2 = heard[attemptIndex];
|
|
670
671
|
entries.push({
|
|
671
672
|
reference: "",
|
|
@@ -677,7 +678,7 @@ function alignValidated(data, assessment) {
|
|
|
677
678
|
continue;
|
|
678
679
|
}
|
|
679
680
|
const referenceWord = reference[referenceIndex];
|
|
680
|
-
if (step ===
|
|
681
|
+
if (step === _chunkDSDXL3JOcjs.STEP.missing) {
|
|
681
682
|
entries.push({
|
|
682
683
|
itemId: referenceWord.itemId,
|
|
683
684
|
reference: referenceWord.word,
|
|
@@ -730,7 +731,7 @@ function placeInsertions(assessment, entries) {
|
|
|
730
731
|
reference: "",
|
|
731
732
|
// Every insertion is accounted for, including one whose text normalises
|
|
732
733
|
// to nothing: a reader of `wordIndex` must find each word it looks for.
|
|
733
|
-
heard:
|
|
734
|
+
heard: _chunkDSDXL3JOcjs.normalizeDictationText.call(void 0, word.text),
|
|
734
735
|
state: "inserted",
|
|
735
736
|
...accuracyOf(word),
|
|
736
737
|
wordIndex
|
|
@@ -767,9 +768,9 @@ function readResponseRecording(value) {
|
|
|
767
768
|
function spokenWordCount(assessment) {
|
|
768
769
|
const recognized = assessment.recognizedText;
|
|
769
770
|
if (recognized !== void 0 && recognized.trim() !== "") {
|
|
770
|
-
return
|
|
771
|
+
return _chunkDSDXL3JOcjs.countWords.call(void 0, recognized);
|
|
771
772
|
}
|
|
772
|
-
return
|
|
773
|
+
return _chunkDSDXL3JOcjs.countWords.call(void 0,
|
|
773
774
|
assessment.words.filter((word) => word.error !== "omission" && word.error !== "insertion").map((word) => word.text).join(" ")
|
|
774
775
|
);
|
|
775
776
|
}
|
|
@@ -793,9 +794,9 @@ function gradeReadAloud(data, response, assessment, options) {
|
|
|
793
794
|
);
|
|
794
795
|
}
|
|
795
796
|
const rounding = roundingPolicyOf(_optionalChain([options, 'optionalAccess', _6 => _6.rounding]));
|
|
796
|
-
const parsed =
|
|
797
|
+
const parsed = _chunkDSDXL3JOcjs.ReadAloudDataSchema.safeParse(data);
|
|
797
798
|
if (!parsed.success) {
|
|
798
|
-
throw new (0,
|
|
799
|
+
throw new (0, _chunkDSDXL3JOcjs.ActivitySchemaError)(
|
|
799
800
|
"read-aloud",
|
|
800
801
|
parsed.error.issues.map((issue) => ({
|
|
801
802
|
path: issue.path.map(String),
|
|
@@ -805,7 +806,7 @@ function gradeReadAloud(data, response, assessment, options) {
|
|
|
805
806
|
);
|
|
806
807
|
}
|
|
807
808
|
if (isRedacted(data) || isRedacted(parsed.data)) {
|
|
808
|
-
throw new (0,
|
|
809
|
+
throw new (0, _chunkDSDXL3JOcjs.RedactedScoringError)("read-aloud");
|
|
809
810
|
}
|
|
810
811
|
const item = parsed.data;
|
|
811
812
|
const checkedResponse = readResponseRecording(response);
|
|
@@ -827,7 +828,6 @@ function gradeReadAloud(data, response, assessment, options) {
|
|
|
827
828
|
// there is no assessment to read, and no grader to credit.
|
|
828
829
|
details: referenceWordsOf(item).map(({ itemId, word }) => ({
|
|
829
830
|
itemId,
|
|
830
|
-
correct: false,
|
|
831
831
|
outcome: "incorrect-omission",
|
|
832
832
|
learnerResponse: "",
|
|
833
833
|
correctResponse: word,
|
|
@@ -940,7 +940,6 @@ function gradeReadAloud(data, response, assessment, options) {
|
|
|
940
940
|
}
|
|
941
941
|
details.push({
|
|
942
942
|
itemId: entry.itemId,
|
|
943
|
-
correct: entry.state === "correct",
|
|
944
943
|
outcome: entry.state === "correct" ? "correct" : entry.state === "mispronounced" ? "incorrect" : "incorrect-omission",
|
|
945
944
|
learnerResponse: entry.heard,
|
|
946
945
|
correctResponse: entry.reference,
|
|
@@ -958,7 +957,7 @@ function gradeReadAloud(data, response, assessment, options) {
|
|
|
958
957
|
};
|
|
959
958
|
}
|
|
960
959
|
function referenceWordsOf(data) {
|
|
961
|
-
return
|
|
960
|
+
return _chunkDSDXL3JOcjs.dictationReferenceWords.call(void 0, { transcript: data.referenceText })[0];
|
|
962
961
|
}
|
|
963
962
|
|
|
964
963
|
// src/scoring/speech/wav.ts
|
|
@@ -1100,19 +1099,19 @@ function outcomeFromUnscorable(result) {
|
|
|
1100
1099
|
// src/scoring/index.ts
|
|
1101
1100
|
function score(activityType, activityData, learnerResponse, options) {
|
|
1102
1101
|
const rounding = roundingPolicyOf(_optionalChain([options, 'optionalAccess', _9 => _9.rounding]));
|
|
1103
|
-
const descriptor =
|
|
1102
|
+
const descriptor = _chunkDSDXL3JOcjs.getActivityTypeDescriptor.call(void 0, activityType);
|
|
1104
1103
|
if (descriptor === void 0) {
|
|
1105
|
-
throw new (0,
|
|
1104
|
+
throw new (0, _chunkDSDXL3JOcjs.UnknownActivityTypeError)(String(activityType));
|
|
1106
1105
|
}
|
|
1107
1106
|
if (descriptor.scoring.kind === "deferred") {
|
|
1108
|
-
throw new (0,
|
|
1107
|
+
throw new (0, _chunkDSDXL3JOcjs.DeferredScoringError)(descriptor.type);
|
|
1109
1108
|
}
|
|
1110
1109
|
if (isRedacted(activityData)) {
|
|
1111
|
-
throw new (0,
|
|
1110
|
+
throw new (0, _chunkDSDXL3JOcjs.RedactedScoringError)(descriptor.type);
|
|
1112
1111
|
}
|
|
1113
1112
|
const result = descriptor.scoring.score(activityData, learnerResponse);
|
|
1114
1113
|
if (!Number.isFinite(result.score)) {
|
|
1115
|
-
throw new (0,
|
|
1114
|
+
throw new (0, _chunkDSDXL3JOcjs.RedactedScoringError)(descriptor.type);
|
|
1116
1115
|
}
|
|
1117
1116
|
const passed = computePassThreshold(activityData, result.score, rounding);
|
|
1118
1117
|
return { ...result, passed, feedback: _nullishCoalesce(result.feedback, () => ( selectFeedback(activityData, passed))) };
|
|
@@ -1129,7 +1128,7 @@ function evaluate(data, response, options) {
|
|
|
1129
1128
|
}
|
|
1130
1129
|
function evaluateAnswer(data, response, rounding) {
|
|
1131
1130
|
const type = data.type;
|
|
1132
|
-
const descriptor = typeof type === "string" ?
|
|
1131
|
+
const descriptor = typeof type === "string" ? _chunkDSDXL3JOcjs.getActivityTypeDescriptor.call(void 0, type) : void 0;
|
|
1133
1132
|
if (descriptor === void 0) {
|
|
1134
1133
|
return {
|
|
1135
1134
|
status: "unscorable",
|
|
@@ -1254,4 +1253,4 @@ function costed(data, opening, graded, rules, rounding) {
|
|
|
1254
1253
|
|
|
1255
1254
|
|
|
1256
1255
|
exports.isGradeInRange = isGradeInRange; exports.DEFAULT_ITEM_SCORING_POLICY = DEFAULT_ITEM_SCORING_POLICY; exports.ITEM_SCORING_MAX_RETRIES = ITEM_SCORING_MAX_RETRIES; exports.validateItemScoringPolicy = validateItemScoringPolicy; exports.resolveItemScoringPolicy = resolveItemScoringPolicy; exports.scoreTries = scoreTries; exports.roundGrade = roundGrade; exports.gte = gte; exports.classifyBand = classifyBand; exports.DEFAULT_PASS_THRESHOLD = DEFAULT_PASS_THRESHOLD; exports.computePassThreshold = computePassThreshold; exports.gradeFromRubric = gradeFromRubric; exports.outcomeFromGrade = outcomeFromGrade; exports.hasGrade = hasGrade; exports.composeAssessmentScore = composeAssessmentScore; exports.diffDictationChars = diffDictationChars; exports.validateSpeechAssessment = validateSpeechAssessment; exports.alignReadAloud = alignReadAloud; exports.gradeReadAloud = gradeReadAloud; exports.inspectWav = inspectWav; exports.outcomeFromUnscorable = outcomeFromUnscorable; exports.score = score; exports.evaluate = evaluate; exports.evaluateTries = evaluateTries;
|
|
1257
|
-
//# sourceMappingURL=chunk-
|
|
1256
|
+
//# sourceMappingURL=chunk-VSH7ECQL.cjs.map
|