@intellectif/lk-core 1.6.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/{ai-i3jwXnpX.d.ts → ai-Cnm0qE8v.d.cts} +334 -16
  3. package/dist/ai-Cnm0qE8v.d.cts.map +1 -0
  4. package/dist/{ai-i3jwXnpX.d.cts → ai-Cnm0qE8v.d.ts} +334 -16
  5. package/dist/ai-Cnm0qE8v.d.ts.map +1 -0
  6. package/dist/ai-check.cjs +88 -7
  7. package/dist/ai-check.cjs.map +1 -1
  8. package/dist/ai-check.d.cts +11 -7
  9. package/dist/ai-check.d.cts.map +1 -1
  10. package/dist/ai-check.d.ts +11 -7
  11. package/dist/ai-check.d.ts.map +1 -1
  12. package/dist/ai-check.js +88 -7
  13. package/dist/ai-check.js.map +1 -1
  14. package/dist/{ai-writing-C4YRVrFi.cjs → ai-writing-Bh2Y53cY.cjs} +2567 -203
  15. package/dist/ai-writing-Bh2Y53cY.cjs.map +1 -0
  16. package/dist/{ai-writing-OJ5m_1EB.js → ai-writing-DeIV-GfN.js} +2428 -172
  17. package/dist/ai-writing-DeIV-GfN.js.map +1 -0
  18. package/dist/{index-Dj92T76o.d.ts → index-CtoPhJAH.d.ts} +2 -8
  19. package/dist/index-CtoPhJAH.d.ts.map +1 -0
  20. package/dist/{index--U7hHV0s.d.cts → index-DMh3OYSb.d.cts} +2 -8
  21. package/dist/index-DMh3OYSb.d.cts.map +1 -0
  22. package/dist/{index-WBHSrIUU.d.ts → index-HXvg0YRQ.d.ts} +5 -12
  23. package/dist/index-HXvg0YRQ.d.ts.map +1 -0
  24. package/dist/{index-CFRgZLhH.d.cts → index-fZ67k-WR.d.cts} +5 -12
  25. package/dist/index-fZ67k-WR.d.cts.map +1 -0
  26. package/dist/{index-DeD8EI1D.d.ts → index-g-EWIugp.d.ts} +2 -42
  27. package/dist/index-g-EWIugp.d.ts.map +1 -0
  28. package/dist/{index-Uh1u57yq.d.cts → index-oST8GBFa.d.cts} +2 -42
  29. package/dist/index-oST8GBFa.d.cts.map +1 -0
  30. package/dist/index.cjs +287 -20
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +227 -71
  33. package/dist/index.d.cts.map +1 -1
  34. package/dist/index.d.ts +227 -71
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +276 -21
  37. package/dist/index.js.map +1 -1
  38. package/dist/{item-group-BbvvH_Ob.js → item-group-BceUoxoM.js} +4 -6
  39. package/dist/{item-group-BbvvH_Ob.js.map → item-group-BceUoxoM.js.map} +1 -1
  40. package/dist/{item-group-CQRbb8Rx.cjs → item-group-KV4hYu3a.cjs} +4 -6
  41. package/dist/{item-group-CQRbb8Rx.cjs.map → item-group-KV4hYu3a.cjs.map} +1 -1
  42. package/dist/{registry-Ih5n4PVZ.cjs → registry-Cymq7Ah0.cjs} +97 -92
  43. package/dist/registry-Cymq7Ah0.cjs.map +1 -0
  44. package/dist/{registry-B23o9UWh.js → registry-tkacvJUI.js} +79 -91
  45. package/dist/registry-tkacvJUI.js.map +1 -0
  46. package/dist/{schemas-yId-bJhD.js → schemas-DM8sN_f0.js} +5 -7
  47. package/dist/{schemas-yId-bJhD.js.map → schemas-DM8sN_f0.js.map} +1 -1
  48. package/dist/{schemas-Q2dyOgc2.cjs → schemas-wDzbKdKa.cjs} +5 -7
  49. package/dist/{schemas-Q2dyOgc2.cjs.map → schemas-wDzbKdKa.cjs.map} +1 -1
  50. package/dist/schemas.cjs +3 -3
  51. package/dist/schemas.d.cts +1 -1
  52. package/dist/schemas.d.ts +1 -1
  53. package/dist/schemas.js +3 -3
  54. package/dist/{scoring-CaPS6q8b.cjs → scoring-D9tFvPEG.cjs} +22 -42
  55. package/dist/{scoring-CaPS6q8b.cjs.map → scoring-D9tFvPEG.cjs.map} +1 -1
  56. package/dist/{scoring-CLMdk8i9.js → scoring-DIBmo_cy.js} +22 -42
  57. package/dist/{scoring-CLMdk8i9.js.map → scoring-DIBmo_cy.js.map} +1 -1
  58. package/dist/scoring.cjs +2 -2
  59. package/dist/scoring.d.cts +2 -2
  60. package/dist/scoring.d.ts +2 -2
  61. package/dist/scoring.js +2 -2
  62. package/dist/{xapi-BX0hvRRB.cjs → xapi-Dyqcl3JK.cjs} +5 -8
  63. package/dist/{xapi-BX0hvRRB.cjs.map → xapi-Dyqcl3JK.cjs.map} +1 -1
  64. package/dist/{xapi-Cdk9fTvV.js → xapi-gCAo549Q.js} +5 -8
  65. package/dist/{xapi-Cdk9fTvV.js.map → xapi-gCAo549Q.js.map} +1 -1
  66. package/dist/xapi.cjs +1 -1
  67. package/dist/xapi.d.cts +1 -1
  68. package/dist/xapi.d.ts +1 -1
  69. package/dist/xapi.js +1 -1
  70. package/package.json +2 -2
  71. package/vectors/README.md +23 -0
  72. package/vectors/variants.d.mts +20 -0
  73. package/vectors/variants.json +5753 -0
  74. package/vectors/variants.mjs +67 -0
  75. package/dist/ai-i3jwXnpX.d.cts.map +0 -1
  76. package/dist/ai-i3jwXnpX.d.ts.map +0 -1
  77. package/dist/ai-writing-C4YRVrFi.cjs.map +0 -1
  78. package/dist/ai-writing-OJ5m_1EB.js.map +0 -1
  79. package/dist/index--U7hHV0s.d.cts.map +0 -1
  80. package/dist/index-CFRgZLhH.d.cts.map +0 -1
  81. package/dist/index-DeD8EI1D.d.ts.map +0 -1
  82. package/dist/index-Dj92T76o.d.ts.map +0 -1
  83. package/dist/index-Uh1u57yq.d.cts.map +0 -1
  84. package/dist/index-WBHSrIUU.d.ts.map +0 -1
  85. package/dist/registry-B23o9UWh.js.map +0 -1
  86. package/dist/registry-Ih5n4PVZ.cjs.map +0 -1
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import { A as AI_COACHING_MAX_TIP_LENGTH, B as aiHintRequest, C as createItemGroupDraft, D as listMediaRefs, E as validateItemGroupDraft, F as AI_HINT_TYPES, G as hintRevealsAnswer, H as buildAiFacts, I as AI_TEXT_MAX_LENGTH, K as readAiTextResult, L as aiAllowedByContent, M as aiCoachingRequest, N as checkAiCoaching, O as mapMediaRefs, P as AI_EXPLANATION_TYPES, R as aiExplanationRequest, S as createInteractiveVideoDraft, T as critiqueItemGroupDraft, U as checkAiExplanation, V as aiSupports, W as checkAiHint, _ as aiCritiqueRequest, a as checkAiGrading, b as critiqueDraft, c as AI_DRAFTS_MAX_COUNT, d as aiDraftsRequest, f as checkAiDrafts, g as AI_CRITIQUE_MAX_FINDINGS, h as AI_CRITIQUE_MAX_FIELD_LENGTH, i as aiGradingRequest, j as AI_COACHING_MAX_WORDS, k as transcriptOf, l as AI_DRAFTS_MAX_REPAIRS, m as interactiveVideoFromDrafts, n as checkAiWritingFeedback, o as AI_WRITING_MAX_CORRECTIONS, p as generateDrafts, q as AI_MATERIAL_MAX_LENGTH, r as AI_GRADING_PROBABILITY_TOLERANCE, s as AI_WRITING_MAX_FIELD_LENGTH, t as aiWritingFeedbackRequest, u as aiDraftsRepairRequest, v as checkAiCritique, w as critiqueDrafts, x as validateDraft, y as createDraft, z as aiGradeOf } from "./ai-writing-OJ5m_1EB.js";
2
- import { $ as DeferredScoringError, C as READ_ALOUD_MAX_TAKES, L as countWords, M as matchText, Q as ActivitySchemaError, S as READ_ALOUD_MAX_SECONDS, T as SPEECH_ASSESSMENT_MAX_WORDS, Tt as SCRIPT_MAX_TURNS, a as readSchema, at as dictationReferenceWords, b as READ_ALOUD_MAX_DIMENSION_WEIGHT, c as dictationType, ct as DICTATION_MAX_EQUIVALENCE_LENGTH, d as multipleChoiceType, et as RedactedScoringError, f as readAloudType, gt as isBuiltInActivityType, h as defineActivityType, j as levenshteinDistance, l as fillInTheBlanksType, lt as DICTATION_MAX_TEXT_LENGTH, m as guardOf, mt as BUILT_IN_ACTIVITY_TYPES, n as registerActivityType, ot as DICTATION_MAX_ACCEPTED_TRANSCRIPTS, p as writtenResponseType, r as registeredActivityTypes, rt as alignDictation, s as MEDIA_FIELD_POLICY, st as DICTATION_MAX_EQUIVALENCES, t as getActivityTypeDescriptor, tt as UnknownActivityTypeError, u as gapSelectType, ut as DICTATION_MAX_TRANSCRIPT_LENGTH, x as READ_ALOUD_MAX_REFERENCE_LENGTH } from "./registry-B23o9UWh.js";
3
- import { A as DEFAULT_ITEM_SCORING_POLICY, C as contentHash, D as classifyBand, E as computePassThreshold, M as resolveItemScoringPolicy, N as scoreTries, O as gte, P as validateItemScoringPolicy, S as canonicalJson, T as DEFAULT_PASS_THRESHOLD, _ as gradeFromRubric, a as inspectWav, b as calibrationReport, c as validateSpeechAssessment, d as inAuditSample, f as outcomeFromRoutedGrade, g as gradeFromJudgement, h as seededShuffle, i as outcomeFromUnscorable, j as ITEM_SCORING_MAX_RETRIES, k as roundGrade, l as diffDictationChars, m as hashSeed, n as evaluateTries, o as gradeReadAloud, p as routeGrade, r as score, s as alignReadAloud, t as evaluate, u as composeAssessmentScore, v as hasGrade, w as fingerprint, x as graderConfiguration, y as outcomeFromGrade } from "./scoring-CLMdk8i9.js";
4
- import { a as INTERACTIVE_VIDEO_ITEM_TYPES, c as TIMELINE_MAX_QUIZZES, i as validateItemGroup, l as TIMELINE_MAX_TITLE_LENGTH, n as RedactedItemGroupSchema, o as TIMELINE_MAX_CHAPTERS, s as TIMELINE_MAX_ITEMS, u as isInteractiveVideoItemType } from "./item-group-BbvvH_Ob.js";
5
- import { a as dictationJsonSchema, c as itemGroupJsonSchema, d as readAloudJsonSchema, f as stimulusJsonSchema, i as contentJsonSchema, l as jsonSchemaFor, n as validateMedia, o as fillInTheBlanksJsonSchema, p as writtenResponseJsonSchema, r as validateOptionMedia, s as gapSelectJsonSchema, t as validateActivity, u as multipleChoiceJsonSchema } from "./schemas-yId-bJhD.js";
6
- import { a as validateXAPIStatement, i as XAPI_VERB_DISPLAY, n as xapiDefinitionFor, r as XAPIVerb, t as xAPIBuilder } from "./xapi-Cdk9fTvV.js";
7
- //#region src/delivery.ts
1
+ import { $ as AI_HINT_TYPES, A as createInteractiveVideoDraft, B as sharedAnswers, C as AI_CRITIQUE_MAX_FIELD_LENGTH, D as createDraft, E as checkAiCritique, F as AI_VARIANT_LENGTH_BANDS, G as listMediaRefs, H as structureMisses, I as AI_VARIANT_NEAR_COPY_THRESHOLD, J as AI_COACHING_MAX_TIP_LENGTH, K as mapMediaRefs, L as keysOf, M as critiqueDrafts, N as critiqueItemGroupDraft, O as critiqueDraft, P as validateItemGroupDraft, Q as AI_EXPLANATION_TYPES, R as lengthMisses, S as interactiveVideoFromDrafts, T as aiCritiqueRequest, U as textSimilarity, V as shownScore, W as textsOf, X as aiCoachingRequest, Y as AI_COACHING_MAX_WORDS, Z as checkAiCoaching, _ as AI_DRAFTS_MAX_REPAIRS, a as AI_VARIANTS_MAX_REPAIRS, at as aiSupports, b as checkAiDrafts, c as aiVariantsRequest, ct as checkAiHint, d as AI_GRADING_PROBABILITY_TOLERANCE, dt as AI_MATERIAL_MAX_LENGTH, et as AI_TEXT_MAX_LENGTH, f as aiGradingRequest, g as AI_DRAFTS_MAX_COUNT, h as AI_WRITING_MAX_FIELD_LENGTH, i as AI_VARIANTS_MAX_MATERIAL_LENGTH, it as aiHintRequest, j as createItemGroupDraft, k as validateDraft, l as checkAiVariants, lt as hintRevealsAnswer, m as AI_WRITING_MAX_CORRECTIONS, n as checkAiWritingFeedback, nt as aiExplanationRequest, o as AI_VARIANT_CODE_SEVERITY, ot as buildAiFacts, p as checkAiGrading, q as transcriptOf, r as AI_VARIANTS_MAX_EXEMPLARS, rt as aiGradeOf, s as aiVariantsRepairRequest, st as checkAiExplanation, t as aiWritingFeedbackRequest, tt as aiAllowedByContent, u as generateVariants, ut as readAiTextResult, v as aiDraftsRepairRequest, w as AI_CRITIQUE_MAX_FINDINGS, x as generateDrafts, y as aiDraftsRequest, z as optionSet } from "./ai-writing-DeIV-GfN.js";
2
+ import { $ as ActivitySchemaError, C as READ_ALOUD_MAX_TAKES, M as levenshteinDistance, N as matchText, Ot as SCRIPT_MAX_TURNS, R as countWords, S as READ_ALOUD_MAX_SECONDS, T as SPEECH_ASSESSMENT_MAX_WORDS, _t as isBuiltInActivityType, a as readSchema, b as READ_ALOUD_MAX_DIMENSION_WEIGHT, c as dictationType, ct as DICTATION_MAX_EQUIVALENCES, d as multipleChoiceType, dt as DICTATION_MAX_TRANSCRIPT_LENGTH, et as DeferredScoringError, f as readAloudType, h as defineActivityType, ht as BUILT_IN_ACTIVITY_TYPES, it as alignDictation, l as fillInTheBlanksType, lt as DICTATION_MAX_EQUIVALENCE_LENGTH, m as guardOf, n as registerActivityType, nt as UnknownActivityTypeError, ot as dictationReferenceWords, p as writtenResponseType, r as registeredActivityTypes, s as MEDIA_FIELD_POLICY, st as DICTATION_MAX_ACCEPTED_TRANSCRIPTS, t as getActivityTypeDescriptor, tt as RedactedScoringError, u as gapSelectType, ut as DICTATION_MAX_TEXT_LENGTH, x as READ_ALOUD_MAX_REFERENCE_LENGTH } from "./registry-tkacvJUI.js";
3
+ import { A as DEFAULT_ITEM_SCORING_POLICY, C as contentHash, D as classifyBand, E as computePassThreshold, M as resolveItemScoringPolicy, N as scoreTries, O as gte, P as validateItemScoringPolicy, S as canonicalJson, T as DEFAULT_PASS_THRESHOLD, _ as gradeFromRubric, a as inspectWav, b as calibrationReport, c as validateSpeechAssessment, d as inAuditSample, f as outcomeFromRoutedGrade, g as gradeFromJudgement, h as seededShuffle, i as outcomeFromUnscorable, j as ITEM_SCORING_MAX_RETRIES, k as roundGrade, l as diffDictationChars, m as hashSeed, n as evaluateTries, o as gradeReadAloud, p as routeGrade, r as score, s as alignReadAloud, t as evaluate, u as composeAssessmentScore, v as hasGrade, w as fingerprint, x as graderConfiguration, y as outcomeFromGrade } from "./scoring-DIBmo_cy.js";
4
+ import { a as INTERACTIVE_VIDEO_ITEM_TYPES, c as TIMELINE_MAX_QUIZZES, i as validateItemGroup, l as TIMELINE_MAX_TITLE_LENGTH, n as RedactedItemGroupSchema, o as TIMELINE_MAX_CHAPTERS, s as TIMELINE_MAX_ITEMS, u as isInteractiveVideoItemType } from "./item-group-BceUoxoM.js";
5
+ import { a as dictationJsonSchema, c as itemGroupJsonSchema, d as readAloudJsonSchema, f as stimulusJsonSchema, i as contentJsonSchema, l as jsonSchemaFor, n as validateMedia, o as fillInTheBlanksJsonSchema, p as writtenResponseJsonSchema, r as validateOptionMedia, s as gapSelectJsonSchema, t as validateActivity, u as multipleChoiceJsonSchema } from "./schemas-DM8sN_f0.js";
6
+ import { a as validateXAPIStatement, i as XAPI_VERB_DISPLAY, n as xapiDefinitionFor, r as XAPIVerb, t as xAPIBuilder } from "./xapi-gCAo549Q.js";
7
+ // src/delivery.ts
8
8
  /**
9
9
  * The policy that restricts nothing: every setting `true`. It is what an absent
10
10
  * policy resolves to, and it is exactly the behaviour the SDK had before
@@ -158,8 +158,7 @@ function validateDeliveryPolicy(policy) {
158
158
  data: given
159
159
  };
160
160
  }
161
- //#endregion
162
- //#region src/item-group.ts
161
+ // src/item-group.ts
163
162
  /** Narrows a sequence entry to an item group. */
164
163
  function isItemGroup(entry) {
165
164
  return entry.type === "item-group";
@@ -283,8 +282,7 @@ function flattenSequence(entries, options = {}) {
283
282
  }
284
283
  return slots;
285
284
  }
286
- //#endregion
287
- //#region src/media-budget.ts
285
+ // src/media-budget.ts
288
286
  /**
289
287
  * The authored-entry prefix of a slot id: `"3"` for both `"3"` and `"3.1"`.
290
288
  *
@@ -398,8 +396,7 @@ function restoreMediaPlayLedger(plan, stored) {
398
396
  if (stored.planHash !== plan.planHash) throw new Error(`restoreMediaPlayLedger: this ledger belongs to a different paper (ledger ${stored.planHash}, plan ${plan.planHash}). Restoring it would hold the learner to a play budget from an exam they never sat.`);
399
397
  return serializeMediaPlayLedger(plan, stored.entries, { ...stored.savedAt !== void 0 ? { savedAt: stored.savedAt } : {} });
400
398
  }
401
- //#endregion
402
- //#region src/attempt-plan.ts
399
+ // src/attempt-plan.ts
403
400
  /**
404
401
  * The activity as CONTENT, with its slot key removed.
405
402
  *
@@ -650,8 +647,7 @@ function scoredItemsFromPlan(plan, outcomes, options = {}) {
650
647
  outcome: Object.hasOwn(outcomes, slot.slotId) ? outcomes[slot.slotId] : missingOutcome(slot, policy)
651
648
  }));
652
649
  }
653
- //#endregion
654
- //#region src/attempt-state.ts
650
+ // src/attempt-state.ts
655
651
  /**
656
652
  * A DEEP copy of the responses.
657
653
  *
@@ -775,8 +771,7 @@ function diffResponses(before, after) {
775
771
  }
776
772
  return entries;
777
773
  }
778
- //#endregion
779
- //#region src/redact.ts
774
+ // src/redact.ts
780
775
  /** Merges per-call overrides over a registered policy, one level deep. */
781
776
  function mergePolicy(base, overrides) {
782
777
  if (overrides === void 0) return base;
@@ -1043,8 +1038,7 @@ function withoutAuthorOnly(entry) {
1043
1038
  ...Array.isArray(items) ? { items: items.map((item) => withoutAuthorOnlyValue(item, policyOf(item?.type))) } : items !== void 0 ? { items } : {}
1044
1039
  };
1045
1040
  }
1046
- //#endregion
1047
- //#region src/timeline.ts
1041
+ // src/timeline.ts
1048
1042
  /**
1049
1043
  * Reads a stored {@link MediaProgress}. Never throws: anything that is not one
1050
1044
  * — a string, a stale shape, an object whose getters throw — answers `null`,
@@ -1098,7 +1092,268 @@ function composeTimelineScore(entryKey, items, policy) {
1098
1092
  items: own
1099
1093
  }], policy);
1100
1094
  }
1101
- //#endregion
1102
- export { AI_COACHING_MAX_TIP_LENGTH, AI_COACHING_MAX_WORDS, AI_CRITIQUE_MAX_FIELD_LENGTH, AI_CRITIQUE_MAX_FINDINGS, AI_DRAFTS_MAX_COUNT, AI_DRAFTS_MAX_REPAIRS, AI_EXPLANATION_TYPES, AI_GRADING_PROBABILITY_TOLERANCE, AI_HINT_TYPES, AI_MATERIAL_MAX_LENGTH, AI_TEXT_MAX_LENGTH, AI_WRITING_MAX_CORRECTIONS, AI_WRITING_MAX_FIELD_LENGTH, ActivitySchemaError, BUILT_IN_ACTIVITY_TYPES, DEFAULT_ITEM_SCORING_POLICY, DEFAULT_PASS_THRESHOLD, DICTATION_MAX_ACCEPTED_TRANSCRIPTS, DICTATION_MAX_EQUIVALENCES, DICTATION_MAX_EQUIVALENCE_LENGTH, DICTATION_MAX_TEXT_LENGTH, DICTATION_MAX_TRANSCRIPT_LENGTH, DeferredScoringError, INTERACTIVE_VIDEO_ITEM_TYPES, ITEM_SCORING_MAX_RETRIES, OPEN_DELIVERY_POLICY, READ_ALOUD_MAX_DIMENSION_WEIGHT, READ_ALOUD_MAX_REFERENCE_LENGTH, READ_ALOUD_MAX_SECONDS, READ_ALOUD_MAX_TAKES, RedactedScoringError, SCRIPT_MAX_TURNS, SPEECH_ASSESSMENT_MAX_WORDS, TIMELINE_MAX_CHAPTERS, TIMELINE_MAX_ITEMS, TIMELINE_MAX_QUIZZES, TIMELINE_MAX_TITLE_LENGTH, UnknownActivityTypeError, XAPIVerb, XAPI_VERB_DISPLAY, aiAllowedByContent, aiCoachingRequest, aiCritiqueRequest, aiDraftsRepairRequest, aiDraftsRequest, aiExplanationRequest, aiGradeOf, aiGradingRequest, aiHintRequest, aiSupports, aiWritingFeedbackRequest, alignDictation, alignReadAloud, assertRedacted, assertRedactedItemGroup, buildAiFacts, calibrationReport, canonicalJson, checkAiCoaching, checkAiCritique, checkAiDrafts, checkAiExplanation, checkAiGrading, checkAiHint, checkAiWritingFeedback, classifyBand, combineDeliveryPolicies, composeAssessmentScore, composeTimelineScore, computePassThreshold, contentHash, contentJsonSchema, countWords, createDraft, createInteractiveVideoDraft, createItemGroupDraft, critiqueDraft, critiqueDrafts, critiqueItemGroupDraft, defineActivityType, dictationJsonSchema, dictationReferenceWords, dictationType, diffDictationChars, diffResponses, entryKeyOf, evaluate, evaluateTries, fillInTheBlanksJsonSchema, fillInTheBlanksType, fingerprint, flattenSequence, gapSelectJsonSchema, gapSelectType, generateDrafts, getActivityTypeDescriptor, gradeFromJudgement, gradeFromRubric, gradeReadAloud, graderConfiguration, gte, hasGrade, hashSeed, hintRevealsAnswer, inAuditSample, inspectWav, interactiveVideoFromDrafts, isBuiltInActivityType, isInteractiveVideoItemType, isItemGroup, itemGroupJsonSchema, jsonSchemaFor, levenshteinDistance, listMediaRefs, mapMediaRefs, matchText, multipleChoiceJsonSchema, multipleChoiceType, outcomeFromGrade, outcomeFromRoutedGrade, outcomeFromUnscorable, planAttempt, planMediaBudgets, readAiTextResult, readAloudJsonSchema, readAloudType, readMediaProgress, redact, redactItemGroup, registerActivityType, registeredActivityTypes, resolveDeliveryPolicy, resolveItemScoringPolicy, resolvePlaybackPolicy, restoreAttemptState, restoreMediaPlayLedger, roundGrade, routeGrade, score, scoreTries, scoredItemsFromPlan, seededShuffle, serializeAttemptState, serializeMediaPlayLedger, slotMediaKey, stimulusJsonSchema, stimulusMediaKey, transcriptOf, validateActivity, validateDeliveryPolicy, validateDraft, validateItemGroup, validateItemGroupDraft, validateItemScoringPolicy, validateMedia, validateOptionMedia, validateSpeechAssessment, validateXAPIStatement, verifyAttemptPlan, withoutAuthorOnly, writtenResponseJsonSchema, writtenResponseType, xAPIBuilder, xapiDefinitionFor };
1095
+ // src/versions.ts
1096
+ /**
1097
+ * Every code `critiqueVersions` reports, and its severity: a contract, as the
1098
+ * critic's codes are. `versions_shared_media` is `advice` for a picture both
1099
+ * forms may show, and a `warning` otherwise.
1100
+ */
1101
+ const VERSION_FINDING_SEVERITY = {
1102
+ versions_section_mismatch: "invalid",
1103
+ versions_entry_count: "invalid",
1104
+ versions_type_mismatch: "invalid",
1105
+ versions_structure_mismatch: "invalid",
1106
+ versions_objectives_mismatch: "warning",
1107
+ versions_length_out_of_band: "warning",
1108
+ versions_near_copy: "warning",
1109
+ versions_same_key: "warning",
1110
+ versions_shared_media: "warning"
1111
+ };
1112
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1113
+ const listOf = (value) => Array.isArray(value) ? value : [];
1114
+ const stringOf = (value) => typeof value === "string" ? value : "";
1115
+ const key = (path) => path.join(".");
1116
+ function report(code, message, a, b, severity = VERSION_FINDING_SEVERITY[code]) {
1117
+ return {
1118
+ path: [...b ?? a ?? []],
1119
+ message,
1120
+ code,
1121
+ severity,
1122
+ ...a !== void 0 ? { a: [...a] } : {},
1123
+ ...b !== void 0 ? { b: [...b] } : {}
1124
+ };
1125
+ }
1126
+ function entriesOf(paper) {
1127
+ return listOf(paper?.sections).flatMap((section, s) => listOf(isRecord(section) ? section.entries : void 0).flatMap((value, index) => isRecord(value) ? [{
1128
+ path: [
1129
+ "sections",
1130
+ String(s),
1131
+ "entries",
1132
+ String(index)
1133
+ ],
1134
+ section: s,
1135
+ index,
1136
+ value
1137
+ }] : []));
1138
+ }
1139
+ /** A group's items, or the entry itself: the slots a learner answers. */
1140
+ const itemsOf = (entry) => entry.value.type === "item-group" ? listOf(entry.value.items).flatMap((item, at) => isRecord(item) ? [{
1141
+ path: [
1142
+ ...entry.path,
1143
+ "items",
1144
+ String(at)
1145
+ ],
1146
+ value: item
1147
+ }] : []) : [{
1148
+ path: entry.path,
1149
+ value: entry.value
1150
+ }];
1151
+ const typeName = (value) => stringOf(value.type) || "no type";
1152
+ function pairEntries(aEntries, bEntries, given) {
1153
+ const pairs = [];
1154
+ const takenA = /* @__PURE__ */ new Set();
1155
+ const takenB = /* @__PURE__ */ new Set();
1156
+ const add = (a, b, by) => {
1157
+ pairs.push({
1158
+ a,
1159
+ b,
1160
+ by
1161
+ });
1162
+ takenA.add(key(a.path));
1163
+ takenB.add(key(b.path));
1164
+ };
1165
+ const aByPath = new Map(aEntries.map((one) => [key(one.path), one]));
1166
+ const bByPath = new Map(bEntries.map((one) => [key(one.path), one]));
1167
+ for (const pair of given ?? []) {
1168
+ const a = aByPath.get(key(pair.a.map(String)));
1169
+ const b = bByPath.get(key(pair.b.map(String)));
1170
+ if (a !== void 0 && b !== void 0 && !takenA.has(key(a.path)) && !takenB.has(key(b.path))) add(a, b, "pairs");
1171
+ }
1172
+ for (const a of aEntries) {
1173
+ const slotKey = a.value.slotKey;
1174
+ if (takenA.has(key(a.path)) || typeof slotKey !== "string") continue;
1175
+ const b = bEntries.find((one) => one.value.slotKey === slotKey && !takenB.has(key(one.path)));
1176
+ if (b !== void 0) add(a, b, "slotKey");
1177
+ }
1178
+ for (const a of aEntries) {
1179
+ if (takenA.has(key(a.path))) continue;
1180
+ const b = bEntries.find((one) => one.section === a.section && one.index === a.index && !takenB.has(key(one.path)));
1181
+ if (b !== void 0) add(a, b, "position");
1182
+ }
1183
+ return pairs;
1184
+ }
1185
+ /** A paired group's items, by their own `slotKey` when both have one, else by position. */
1186
+ function pairItems(pair) {
1187
+ const aItems = itemsOf(pair.a);
1188
+ const bItems = itemsOf(pair.b);
1189
+ if (pair.a.value.type !== "item-group" || pair.b.value.type !== "item-group") {
1190
+ const one = aItems[0];
1191
+ const two = bItems[0];
1192
+ return one !== void 0 && two !== void 0 ? [{
1193
+ a: one,
1194
+ b: two,
1195
+ by: pair.by
1196
+ }] : [];
1197
+ }
1198
+ const taken = /* @__PURE__ */ new Set();
1199
+ const pairs = [];
1200
+ aItems.forEach((a) => {
1201
+ const slotKey = a.value.slotKey;
1202
+ const found = typeof slotKey === "string" ? bItems.findIndex((b, index) => b.value.slotKey === slotKey && !taken.has(index)) : -1;
1203
+ if (found !== -1) {
1204
+ taken.add(found);
1205
+ pairs.push({
1206
+ a,
1207
+ b: bItems[found],
1208
+ by: "slotKey"
1209
+ });
1210
+ }
1211
+ });
1212
+ aItems.forEach((a, at) => {
1213
+ if (pairs.some((one) => one.a === a) || at >= bItems.length || taken.has(at)) return;
1214
+ taken.add(at);
1215
+ pairs.push({
1216
+ a,
1217
+ b: bItems[at],
1218
+ by: "position"
1219
+ });
1220
+ });
1221
+ return pairs.sort((x, y) => aItems.indexOf(x.a) - aItems.indexOf(y.a));
1222
+ }
1223
+ const objectives = (value) => [...listOf(value.learningObjectives).filter((one) => typeof one === "string")].sort().join("\n");
1224
+ /**
1225
+ * Compares two forms of an exam slot by slot, and the whole of one with the
1226
+ * whole of the other: whether they are parallel — the same blueprint, types,
1227
+ * structure and scoring (`invalid` when not) — and whether a learner who sat
1228
+ * one would meet the other again: a near copy anywhere in the other form, a
1229
+ * shared right answer in a paired slot, the same recording (`warning`), or
1230
+ * the same picture (`advice` while `allowSharedImages`, its default).
1231
+ * Objectives and lengths that differ are warnings.
1232
+ *
1233
+ * Slots are paired by `pairs` (paths from each paper's root, as
1234
+ * `generateVariants`' pairs give them once a host places them), then by
1235
+ * `slotKey`, then by position: the same entry of the same section. A paired
1236
+ * group's items pair by their own `slotKey`, then by position.
1237
+ *
1238
+ * It reads content as stored, finished or not, and never throws.
1239
+ */
1240
+ function critiqueVersions(a, b, options = {}) {
1241
+ const findings = [];
1242
+ const aSections = listOf(a?.sections);
1243
+ const bSections = listOf(b?.sections);
1244
+ if (aSections.length !== bSections.length) findings.push(report("versions_section_mismatch", `The second form has ${bSections.length} sections; the first has ${aSections.length}.`, void 0, void 0));
1245
+ for (let s = 0; s < Math.min(aSections.length, bSections.length); s += 1) {
1246
+ const one = isRecord(aSections[s]) ? aSections[s] : {};
1247
+ const two = isRecord(bSections[s]) ? bSections[s] : {};
1248
+ const at = ["sections", String(s)];
1249
+ if (one.key !== two.key || one.title !== two.title) findings.push(report("versions_section_mismatch", `Section ${s + 1} is "${stringOf(two.title) || stringOf(two.key)}" in the second form and "${stringOf(one.title) || stringOf(one.key)}" in the first.`, at, at));
1250
+ const aCount = listOf(one.entries).length;
1251
+ const bCount = listOf(two.entries).length;
1252
+ if (aCount !== bCount) findings.push(report("versions_entry_count", `Section ${s + 1} has ${bCount} entries in the second form and ${aCount} in the first.`, [...at, "entries"], [...at, "entries"]));
1253
+ }
1254
+ const aEntries = entriesOf(a);
1255
+ const bEntries = entriesOf(b);
1256
+ const pairs = pairEntries(aEntries, bEntries, options.pairs);
1257
+ let structureMismatches = 0;
1258
+ let paired = 0;
1259
+ const slots = [];
1260
+ for (const pair of pairs) {
1261
+ slots.push({
1262
+ a: pair.a.path,
1263
+ b: pair.b.path,
1264
+ by: pair.by
1265
+ });
1266
+ if (pair.a.value.type !== pair.b.value.type) {
1267
+ structureMismatches += 1;
1268
+ findings.push(report("versions_type_mismatch", `The second form has ${typeName(pair.b.value)} here; the first has ${typeName(pair.a.value)}.`, pair.a.path, pair.b.path));
1269
+ continue;
1270
+ }
1271
+ const misses = structureMisses(pair.a.value, pair.b.value);
1272
+ if (misses.length > 0) structureMismatches += 1;
1273
+ for (const miss of misses) {
1274
+ const code = miss.path[miss.path.length - 1] === "type" ? "versions_type_mismatch" : miss.path.length === 1 && miss.path[0] === "items" ? "versions_entry_count" : "versions_structure_mismatch";
1275
+ findings.push(report(code, miss.message, [...pair.a.path, ...miss.path], [...pair.b.path, ...miss.path]));
1276
+ }
1277
+ for (const miss of lengthMisses(pair.a.value, pair.b.value, options.lengthTolerance)) findings.push(report("versions_length_out_of_band", `${miss.words} words; the first form's has ${miss.exemplarWords}, so ${miss.bounds[0]} to ${miss.bounds[1]}.`, [...pair.a.path, ...miss.path], [...pair.b.path, ...miss.path]));
1278
+ const items = pairItems(pair);
1279
+ if (pair.a.value.type === "item-group") for (const item of items) slots.push({
1280
+ a: item.a.path,
1281
+ b: item.b.path,
1282
+ by: item.by
1283
+ });
1284
+ for (const item of items) {
1285
+ if (item.a.value.type !== item.b.value.type) continue;
1286
+ paired += 1;
1287
+ if ((item.a.value.learningObjectives !== void 0 || item.b.value.learningObjectives !== void 0) && objectives(item.a.value) !== objectives(item.b.value)) findings.push(report("versions_objectives_mismatch", "The two forms test different learning objectives here.", item.a.path, item.b.path));
1288
+ const aKeys = keysOf(item.a.value);
1289
+ for (const slot of keysOf(item.b.value)) {
1290
+ const match = aKeys.find((one) => one.slot === slot.slot);
1291
+ const shared = match !== void 0 ? sharedAnswers(slot, match) : [];
1292
+ if (shared.length > 0) findings.push(report("versions_same_key", `The right answer "${shared[0]}" is the same in both forms: a learner who sat the first scores again by memory.`, [...item.a.path, ...match.path], [...item.b.path, ...slot.path]));
1293
+ }
1294
+ }
1295
+ }
1296
+ const pairedA = new Set(slots.flatMap((slot) => slot.a !== null ? [key(slot.a)] : []));
1297
+ const pairedB = new Set(slots.flatMap((slot) => slot.b !== null ? [key(slot.b)] : []));
1298
+ for (const entry of aEntries) for (const one of entry.value.type === "item-group" ? [{ path: entry.path }, ...itemsOf(entry)] : [entry]) if (!pairedA.has(key(one.path))) slots.push({
1299
+ a: one.path,
1300
+ b: null,
1301
+ by: "position"
1302
+ });
1303
+ for (const entry of bEntries) for (const one of entry.value.type === "item-group" ? [{ path: entry.path }, ...itemsOf(entry)] : [entry]) if (!pairedB.has(key(one.path))) slots.push({
1304
+ a: null,
1305
+ b: one.path,
1306
+ by: "position"
1307
+ });
1308
+ const aTexts = aEntries.flatMap((entry) => textsOf(entry.value).map((text) => ({
1309
+ ...text,
1310
+ path: [...entry.path, ...text.path]
1311
+ })));
1312
+ for (const entry of bEntries) for (const text of textsOf(entry.value)) {
1313
+ let best;
1314
+ for (const other of aTexts) {
1315
+ if (other.kind !== text.kind) continue;
1316
+ const score = textSimilarity(other.text, text.text);
1317
+ if (score >= .5 && (best === void 0 || score > best.score)) best = {
1318
+ score,
1319
+ path: other.path
1320
+ };
1321
+ }
1322
+ if (best !== void 0) findings.push(report("versions_near_copy", `This reads like the first form's text here (similarity ${shownScore(best.score)}).`, best.path, [...entry.path, ...text.path]));
1323
+ }
1324
+ const aOptions = aEntries.flatMap((entry) => itemsOf(entry).flatMap((item) => {
1325
+ const set = optionSet(item.value);
1326
+ return set !== null ? [{
1327
+ path: item.path,
1328
+ set: set.join("\n")
1329
+ }] : [];
1330
+ }));
1331
+ for (const entry of bEntries) for (const item of itemsOf(entry)) {
1332
+ const set = optionSet(item.value);
1333
+ const match = set !== null ? aOptions.find((other) => other.set === set.join("\n")) : void 0;
1334
+ if (match !== void 0) findings.push(report("versions_near_copy", "These options are the first form's, in another order.", [...match.path, "options"], [...item.path, "options"]));
1335
+ }
1336
+ const allowImages = options.allowSharedImages !== false;
1337
+ const aFiles = /* @__PURE__ */ new Map();
1338
+ for (const entry of aEntries) for (const ref of listMediaRefs(entry.value)) if (ref.url !== null && ref.url !== "" && !aFiles.has(ref.url)) aFiles.set(ref.url, [...entry.path, ...ref.path.map(String)]);
1339
+ for (const entry of bEntries) for (const ref of listMediaRefs(entry.value)) {
1340
+ const first = ref.url !== null ? aFiles.get(ref.url) : void 0;
1341
+ if (first === void 0) continue;
1342
+ const picture = ref.kind === "image";
1343
+ findings.push(report("versions_shared_media", picture ? "Both forms show this picture." : "Both forms use this file: a learner who sat the first hears or sees it again.", first, [...entry.path, ...ref.path.map(String)], picture && allowImages ? "advice" : "warning"));
1344
+ }
1345
+ const count = (entries) => entries.reduce((sum, entry) => sum + itemsOf(entry).length, 0);
1346
+ return {
1347
+ findings,
1348
+ slots,
1349
+ summary: {
1350
+ aItems: count(aEntries),
1351
+ bItems: count(bEntries),
1352
+ paired,
1353
+ structureMismatches
1354
+ }
1355
+ };
1356
+ }
1357
+ export { AI_COACHING_MAX_TIP_LENGTH, AI_COACHING_MAX_WORDS, AI_CRITIQUE_MAX_FIELD_LENGTH, AI_CRITIQUE_MAX_FINDINGS, AI_DRAFTS_MAX_COUNT, AI_DRAFTS_MAX_REPAIRS, AI_EXPLANATION_TYPES, AI_GRADING_PROBABILITY_TOLERANCE, AI_HINT_TYPES, AI_MATERIAL_MAX_LENGTH, AI_TEXT_MAX_LENGTH, AI_VARIANTS_MAX_EXEMPLARS, AI_VARIANTS_MAX_MATERIAL_LENGTH, AI_VARIANTS_MAX_REPAIRS, AI_VARIANT_CODE_SEVERITY, AI_VARIANT_LENGTH_BANDS, AI_VARIANT_NEAR_COPY_THRESHOLD, AI_WRITING_MAX_CORRECTIONS, AI_WRITING_MAX_FIELD_LENGTH, ActivitySchemaError, BUILT_IN_ACTIVITY_TYPES, DEFAULT_ITEM_SCORING_POLICY, DEFAULT_PASS_THRESHOLD, DICTATION_MAX_ACCEPTED_TRANSCRIPTS, DICTATION_MAX_EQUIVALENCES, DICTATION_MAX_EQUIVALENCE_LENGTH, DICTATION_MAX_TEXT_LENGTH, DICTATION_MAX_TRANSCRIPT_LENGTH, DeferredScoringError, INTERACTIVE_VIDEO_ITEM_TYPES, ITEM_SCORING_MAX_RETRIES, OPEN_DELIVERY_POLICY, READ_ALOUD_MAX_DIMENSION_WEIGHT, READ_ALOUD_MAX_REFERENCE_LENGTH, READ_ALOUD_MAX_SECONDS, READ_ALOUD_MAX_TAKES, RedactedScoringError, SCRIPT_MAX_TURNS, SPEECH_ASSESSMENT_MAX_WORDS, TIMELINE_MAX_CHAPTERS, TIMELINE_MAX_ITEMS, TIMELINE_MAX_QUIZZES, TIMELINE_MAX_TITLE_LENGTH, UnknownActivityTypeError, VERSION_FINDING_SEVERITY, XAPIVerb, XAPI_VERB_DISPLAY, aiAllowedByContent, aiCoachingRequest, aiCritiqueRequest, aiDraftsRepairRequest, aiDraftsRequest, aiExplanationRequest, aiGradeOf, aiGradingRequest, aiHintRequest, aiSupports, aiVariantsRepairRequest, aiVariantsRequest, aiWritingFeedbackRequest, alignDictation, alignReadAloud, assertRedacted, assertRedactedItemGroup, buildAiFacts, calibrationReport, canonicalJson, checkAiCoaching, checkAiCritique, checkAiDrafts, checkAiExplanation, checkAiGrading, checkAiHint, checkAiVariants, checkAiWritingFeedback, classifyBand, combineDeliveryPolicies, composeAssessmentScore, composeTimelineScore, computePassThreshold, contentHash, contentJsonSchema, countWords, createDraft, createInteractiveVideoDraft, createItemGroupDraft, critiqueDraft, critiqueDrafts, critiqueItemGroupDraft, critiqueVersions, defineActivityType, dictationJsonSchema, dictationReferenceWords, dictationType, diffDictationChars, diffResponses, entryKeyOf, evaluate, evaluateTries, fillInTheBlanksJsonSchema, fillInTheBlanksType, fingerprint, flattenSequence, gapSelectJsonSchema, gapSelectType, generateDrafts, generateVariants, getActivityTypeDescriptor, gradeFromJudgement, gradeFromRubric, gradeReadAloud, graderConfiguration, gte, hasGrade, hashSeed, hintRevealsAnswer, inAuditSample, inspectWav, interactiveVideoFromDrafts, isBuiltInActivityType, isInteractiveVideoItemType, isItemGroup, itemGroupJsonSchema, jsonSchemaFor, levenshteinDistance, listMediaRefs, mapMediaRefs, matchText, multipleChoiceJsonSchema, multipleChoiceType, outcomeFromGrade, outcomeFromRoutedGrade, outcomeFromUnscorable, planAttempt, planMediaBudgets, readAiTextResult, readAloudJsonSchema, readAloudType, readMediaProgress, redact, redactItemGroup, registerActivityType, registeredActivityTypes, resolveDeliveryPolicy, resolveItemScoringPolicy, resolvePlaybackPolicy, restoreAttemptState, restoreMediaPlayLedger, roundGrade, routeGrade, score, scoreTries, scoredItemsFromPlan, seededShuffle, serializeAttemptState, serializeMediaPlayLedger, slotMediaKey, stimulusJsonSchema, stimulusMediaKey, transcriptOf, validateActivity, validateDeliveryPolicy, validateDraft, validateItemGroup, validateItemGroupDraft, validateItemScoringPolicy, validateMedia, validateOptionMedia, validateSpeechAssessment, validateXAPIStatement, verifyAttemptPlan, withoutAuthorOnly, writtenResponseJsonSchema, writtenResponseType, xAPIBuilder, xapiDefinitionFor };
1103
1358
 
1104
1359
  //# sourceMappingURL=index.js.map