@intellectif/lk-core 0.11.0 → 0.12.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 +14 -0
- package/dist/{chunk-VBM5H6P5.cjs → chunk-4OHWLCI6.cjs} +8 -8
- package/dist/{chunk-VBM5H6P5.cjs.map → chunk-4OHWLCI6.cjs.map} +1 -1
- package/dist/{chunk-XGCTAQSS.js → chunk-7JMMPE7O.js} +2 -2
- package/dist/{chunk-FIS5KBCE.js → chunk-FVN37GIH.js} +28 -1
- package/dist/chunk-FVN37GIH.js.map +1 -0
- package/dist/{chunk-7ODQRNR4.cjs → chunk-J6POE4HL.cjs} +29 -2
- package/dist/chunk-J6POE4HL.cjs.map +1 -0
- package/dist/{chunk-VTTRXGDD.cjs → chunk-O2IVKTHV.cjs} +4 -4
- package/dist/{chunk-VTTRXGDD.cjs.map → chunk-O2IVKTHV.cjs.map} +1 -1
- package/dist/{chunk-ZTY4UIUD.js → chunk-RK2NVTHZ.js} +2 -2
- package/dist/{chunk-IXXMYJI7.js → chunk-UQPNJP2O.js} +2 -2
- package/dist/{chunk-3FEAEO3D.cjs → chunk-VYE6BYON.cjs} +13 -13
- package/dist/{chunk-3FEAEO3D.cjs.map → chunk-VYE6BYON.cjs.map} +1 -1
- package/dist/index.cjs +294 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -1
- package/dist/index.d.ts +35 -1
- package/dist/index.js +266 -10
- package/dist/index.js.map +1 -1
- package/dist/schemas.cjs +3 -3
- package/dist/schemas.js +2 -2
- package/dist/scoring.cjs +3 -3
- package/dist/scoring.js +2 -2
- package/dist/xapi.cjs +3 -3
- package/dist/xapi.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-7ODQRNR4.cjs.map +0 -1
- package/dist/chunk-FIS5KBCE.js.map +0 -1
- /package/dist/{chunk-XGCTAQSS.js.map → chunk-7JMMPE7O.js.map} +0 -0
- /package/dist/{chunk-ZTY4UIUD.js.map → chunk-RK2NVTHZ.js.map} +0 -0
- /package/dist/{chunk-IXXMYJI7.js.map → chunk-UQPNJP2O.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -10,14 +10,14 @@ import {
|
|
|
10
10
|
outcomeFromGrade,
|
|
11
11
|
roundGrade,
|
|
12
12
|
score
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-RK2NVTHZ.js";
|
|
14
14
|
import {
|
|
15
15
|
XAPIVerb,
|
|
16
16
|
XAPI_VERB_DISPLAY,
|
|
17
17
|
validateXAPIStatement,
|
|
18
18
|
xAPIBuilder,
|
|
19
19
|
xapiDefinitionFor
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-UQPNJP2O.js";
|
|
21
21
|
import {
|
|
22
22
|
ItemGroupSchema,
|
|
23
23
|
RedactedItemGroupSchema,
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
validateActivity,
|
|
33
33
|
validateItemGroup,
|
|
34
34
|
writtenResponseJsonSchema
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-7JMMPE7O.js";
|
|
36
36
|
import {
|
|
37
37
|
ActivitySchemaError,
|
|
38
38
|
BlankConfigSchema,
|
|
@@ -69,11 +69,16 @@ import {
|
|
|
69
69
|
WrittenResponseDataSchema,
|
|
70
70
|
WrittenResponseRubricCriterionSchema,
|
|
71
71
|
WrittenResponseRubricSchema,
|
|
72
|
+
checkMedia,
|
|
72
73
|
countWords,
|
|
74
|
+
covers,
|
|
73
75
|
defineActivityType,
|
|
74
76
|
fillInTheBlanksType,
|
|
75
77
|
getActivityTypeDescriptor,
|
|
78
|
+
isMissingId,
|
|
76
79
|
isRecord,
|
|
80
|
+
isUnset,
|
|
81
|
+
isUnwritten,
|
|
77
82
|
issue,
|
|
78
83
|
levenshteinDistance,
|
|
79
84
|
matchText,
|
|
@@ -82,7 +87,7 @@ import {
|
|
|
82
87
|
registerActivityType,
|
|
83
88
|
registeredActivityTypes,
|
|
84
89
|
writtenResponseType
|
|
85
|
-
} from "./chunk-
|
|
90
|
+
} from "./chunk-FVN37GIH.js";
|
|
86
91
|
|
|
87
92
|
// src/content-hash.ts
|
|
88
93
|
function canonicalJson(value, seen = /* @__PURE__ */ new Set()) {
|
|
@@ -586,6 +591,261 @@ function diffResponses(before, after) {
|
|
|
586
591
|
return entries;
|
|
587
592
|
}
|
|
588
593
|
|
|
594
|
+
// src/authoring/item-group.ts
|
|
595
|
+
function createItemGroupDraft(context) {
|
|
596
|
+
const issued = /* @__PURE__ */ new Set();
|
|
597
|
+
const newId = () => {
|
|
598
|
+
const id = context.newId();
|
|
599
|
+
if (typeof id !== "string" || id === "") {
|
|
600
|
+
throw new Error("createItemGroupDraft: newId() must return a non-empty string.");
|
|
601
|
+
}
|
|
602
|
+
if (issued.has(id)) {
|
|
603
|
+
throw new Error(
|
|
604
|
+
`createItemGroupDraft: newId() returned "${id}" twice. Ids within one draft must differ.`
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
issued.add(id);
|
|
608
|
+
return id;
|
|
609
|
+
};
|
|
610
|
+
return {
|
|
611
|
+
schemaVersion: "1.0",
|
|
612
|
+
type: "item-group",
|
|
613
|
+
id: newId(),
|
|
614
|
+
stimulus: { id: newId(), kind: "text", body: "" },
|
|
615
|
+
items: []
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
function validateItemGroupDraft(draft) {
|
|
619
|
+
const issues = [];
|
|
620
|
+
if (!isRecord(draft)) {
|
|
621
|
+
issues.push(
|
|
622
|
+
issue("ig_not_an_object", [], "A group must be an object with a stimulus and its items.")
|
|
623
|
+
);
|
|
624
|
+
return { status: "invalid", issues };
|
|
625
|
+
}
|
|
626
|
+
issues.push(...checkGroupIdentity(draft));
|
|
627
|
+
if (isRecord(draft.stimulus)) {
|
|
628
|
+
issues.push(...checkStimulus(draft.stimulus));
|
|
629
|
+
} else if (isUnset(draft.stimulus)) {
|
|
630
|
+
issues.push(
|
|
631
|
+
issue(
|
|
632
|
+
"ig_stimulus_required",
|
|
633
|
+
["stimulus"],
|
|
634
|
+
"Add the passage, recording or image the questions are about."
|
|
635
|
+
)
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
issues.push(...checkItems(draft));
|
|
639
|
+
const parsed = ItemGroupSchema.safeParse(draft, { reportInput: true });
|
|
640
|
+
if (!parsed.success) {
|
|
641
|
+
for (const schemaIssue of parsed.error.issues) {
|
|
642
|
+
const path = schemaIssue.path.map(String);
|
|
643
|
+
if (issues.some((known) => covers(known.path, path))) {
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
if (path.length > 0 && refusesEmpty(draft, schemaIssue)) {
|
|
647
|
+
issues.push(
|
|
648
|
+
issue(
|
|
649
|
+
"null_not_allowed",
|
|
650
|
+
path,
|
|
651
|
+
"This field cannot be null. Leave it out if it is optional, or give it a value."
|
|
652
|
+
)
|
|
653
|
+
);
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
issues.push({
|
|
657
|
+
path,
|
|
658
|
+
message: schemaIssue.message,
|
|
659
|
+
code: schemaIssue.code,
|
|
660
|
+
severity: "invalid"
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
const stored = validateItemGroup(draft);
|
|
665
|
+
if (issues.length === 0 && stored.success) {
|
|
666
|
+
return { status: "complete", data: stored.data, issues };
|
|
667
|
+
}
|
|
668
|
+
if (issues.length === 0) {
|
|
669
|
+
for (const error of stored.success ? [] : stored.errors) {
|
|
670
|
+
issues.push({ ...error, severity: "invalid" });
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
const invalid = issues.some((found) => found.severity !== "incomplete");
|
|
674
|
+
return { status: invalid ? "invalid" : "incomplete", issues };
|
|
675
|
+
}
|
|
676
|
+
function checkGroupIdentity(draft) {
|
|
677
|
+
const issues = [];
|
|
678
|
+
if (draft.schemaVersion !== "1.0") {
|
|
679
|
+
issues.push(
|
|
680
|
+
issue(
|
|
681
|
+
"schema_version_invalid",
|
|
682
|
+
["schemaVersion"],
|
|
683
|
+
'The draft must have schemaVersion "1.0". A draft from createItemGroupDraft has it.'
|
|
684
|
+
)
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
if (draft.type !== "item-group") {
|
|
688
|
+
issues.push(issue("type_mismatch", ["type"], `The draft's type must be "item-group".`));
|
|
689
|
+
}
|
|
690
|
+
if (isMissingId(draft.id)) {
|
|
691
|
+
issues.push(issue("id_required", ["id"], "The group has no id."));
|
|
692
|
+
}
|
|
693
|
+
if (!isUnset(draft.shuffle) && draft.shuffle !== "none" && draft.shuffle !== "within-group") {
|
|
694
|
+
issues.push(
|
|
695
|
+
issue(
|
|
696
|
+
"ig_shuffle_invalid",
|
|
697
|
+
["shuffle"],
|
|
698
|
+
'Shuffling is either "none" or "within-group". Leave it out to keep the authored order.'
|
|
699
|
+
)
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
return issues;
|
|
703
|
+
}
|
|
704
|
+
var KINDS = /* @__PURE__ */ new Set(["text", "audio", "video", "image", "mixed"]);
|
|
705
|
+
var MEDIA_FOR_KIND = {
|
|
706
|
+
audio: ["audio"],
|
|
707
|
+
video: ["video", "embed"],
|
|
708
|
+
image: ["image"],
|
|
709
|
+
mixed: ["audio", "video", "image", "embed"]
|
|
710
|
+
};
|
|
711
|
+
function checkStimulus(stimulus) {
|
|
712
|
+
const issues = [];
|
|
713
|
+
const at = (...rest) => ["stimulus", ...rest];
|
|
714
|
+
if (isMissingId(stimulus.id)) {
|
|
715
|
+
issues.push(issue("ig_stimulus_id_required", at("id"), "The stimulus has no id."));
|
|
716
|
+
}
|
|
717
|
+
const kind = stimulus.kind;
|
|
718
|
+
if (isUnwritten(kind)) {
|
|
719
|
+
issues.push(
|
|
720
|
+
issue(
|
|
721
|
+
"ig_stimulus_kind_required",
|
|
722
|
+
at("kind"),
|
|
723
|
+
"Choose what the questions are about: a text, a recording, a video, an image, or a mix."
|
|
724
|
+
)
|
|
725
|
+
);
|
|
726
|
+
} else if (typeof kind !== "string" || !KINDS.has(kind)) {
|
|
727
|
+
if (typeof kind === "string") {
|
|
728
|
+
issues.push(
|
|
729
|
+
issue(
|
|
730
|
+
"ig_stimulus_kind_invalid",
|
|
731
|
+
at("kind"),
|
|
732
|
+
`"${kind}" is not a stimulus kind. Use text, audio, video, image or mixed.`
|
|
733
|
+
)
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
if (isRecord(stimulus.media)) {
|
|
737
|
+
issues.push(...checkMedia(stimulus.media, at("media")));
|
|
738
|
+
}
|
|
739
|
+
return issues;
|
|
740
|
+
}
|
|
741
|
+
const hasBody = typeof stimulus.body === "string" && stimulus.body.trim() !== "";
|
|
742
|
+
const htmlPresent = !isUnset(stimulus.bodyHtml);
|
|
743
|
+
if (!hasBody && (kind === "text" || kind === "mixed" || htmlPresent)) {
|
|
744
|
+
issues.push(
|
|
745
|
+
issue(
|
|
746
|
+
"ig_stimulus_body_required",
|
|
747
|
+
at("body"),
|
|
748
|
+
isUnwritten(stimulus.bodyHtml) ? "Write the passage." : "Write the passage as plain text too. The rich-text version is shown only where a sanitiser is supplied."
|
|
749
|
+
)
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
if (kind !== "text" && isUnset(stimulus.media)) {
|
|
753
|
+
issues.push(
|
|
754
|
+
issue("ig_stimulus_media_required", at("media"), "Add the recording, video or image.")
|
|
755
|
+
);
|
|
756
|
+
} else if (isRecord(stimulus.media)) {
|
|
757
|
+
const allowed = MEDIA_FOR_KIND[kind];
|
|
758
|
+
const mediaType = stimulus.media.type;
|
|
759
|
+
if (allowed !== void 0 && typeof mediaType === "string" && !allowed.includes(mediaType)) {
|
|
760
|
+
issues.push(
|
|
761
|
+
issue(
|
|
762
|
+
"ig_stimulus_media_kind",
|
|
763
|
+
at("media", "type"),
|
|
764
|
+
`A "${kind}" stimulus cannot carry ${mediaType} media. Use ${allowed.join(" or ")}.`
|
|
765
|
+
)
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
issues.push(...checkMedia(stimulus.media, at("media")));
|
|
769
|
+
}
|
|
770
|
+
return issues;
|
|
771
|
+
}
|
|
772
|
+
function checkItems(draft) {
|
|
773
|
+
const issues = [];
|
|
774
|
+
const items = isUnset(draft.items) ? [] : draft.items;
|
|
775
|
+
if (!Array.isArray(items)) {
|
|
776
|
+
return issues;
|
|
777
|
+
}
|
|
778
|
+
if (items.length === 0) {
|
|
779
|
+
issues.push(
|
|
780
|
+
issue("ig_items_required", ["items"], "Add at least one question about this material.")
|
|
781
|
+
);
|
|
782
|
+
return issues;
|
|
783
|
+
}
|
|
784
|
+
const seen = /* @__PURE__ */ new Set();
|
|
785
|
+
const repeated = /* @__PURE__ */ new Set();
|
|
786
|
+
for (const [index, item] of items.entries()) {
|
|
787
|
+
if (!isRecord(item)) {
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
const ordinal = index + 1;
|
|
791
|
+
if (isMissingId(item.id)) {
|
|
792
|
+
issues.push(
|
|
793
|
+
issue("ig_item_id_required", ["items", index, "id"], `Question ${ordinal} has no id.`)
|
|
794
|
+
);
|
|
795
|
+
} else if (typeof item.id === "string") {
|
|
796
|
+
if (seen.has(item.id)) {
|
|
797
|
+
repeated.add(item.id);
|
|
798
|
+
}
|
|
799
|
+
seen.add(item.id);
|
|
800
|
+
}
|
|
801
|
+
if (item.type === "item-group") {
|
|
802
|
+
issues.push(
|
|
803
|
+
issue(
|
|
804
|
+
"ig_item_nested_group",
|
|
805
|
+
["items", index, "type"],
|
|
806
|
+
"Groups do not nest: every item must be an activity."
|
|
807
|
+
)
|
|
808
|
+
);
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
if (isUnwritten(item.type)) {
|
|
812
|
+
issues.push(
|
|
813
|
+
issue(
|
|
814
|
+
"ig_item_type_required",
|
|
815
|
+
["items", index, "type"],
|
|
816
|
+
`Choose what kind of question ${ordinal} is.`
|
|
817
|
+
)
|
|
818
|
+
);
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
if (typeof item.type !== "string") {
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
let result;
|
|
825
|
+
try {
|
|
826
|
+
result = validateDraft(item.type, item);
|
|
827
|
+
} catch {
|
|
828
|
+
issues.push(
|
|
829
|
+
issue(
|
|
830
|
+
"ig_item_type_unknown",
|
|
831
|
+
["items", index, "type"],
|
|
832
|
+
`"${item.type}" is not a registered activity type.`
|
|
833
|
+
)
|
|
834
|
+
);
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
837
|
+
for (const found of result.issues) {
|
|
838
|
+
issues.push({ ...found, path: ["items", String(index), ...found.path] });
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
for (const id of repeated) {
|
|
842
|
+
issues.push(
|
|
843
|
+
issue("ig_item_id_duplicate", ["items"], `More than one question has the id "${id}".`)
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
return issues;
|
|
847
|
+
}
|
|
848
|
+
|
|
589
849
|
// src/authoring/index.ts
|
|
590
850
|
function validateDraft(type, draft) {
|
|
591
851
|
const descriptor = getActivityTypeDescriptor(type);
|
|
@@ -665,12 +925,6 @@ function normalise(found) {
|
|
|
665
925
|
severity: documented ?? (found.severity === "incomplete" ? "incomplete" : "invalid")
|
|
666
926
|
};
|
|
667
927
|
}
|
|
668
|
-
function covers(reported, failed) {
|
|
669
|
-
if (failed.length === 0) {
|
|
670
|
-
return reported.length === 0;
|
|
671
|
-
}
|
|
672
|
-
return failed.every((segment, index) => reported[index] === segment);
|
|
673
|
-
}
|
|
674
928
|
|
|
675
929
|
// src/redact.ts
|
|
676
930
|
function mergePolicy(base, overrides) {
|
|
@@ -937,6 +1191,7 @@ export {
|
|
|
937
1191
|
contentHash,
|
|
938
1192
|
countWords,
|
|
939
1193
|
createDraft,
|
|
1194
|
+
createItemGroupDraft,
|
|
940
1195
|
defineActivityType,
|
|
941
1196
|
diffResponses,
|
|
942
1197
|
entryKeyOf,
|
|
@@ -980,6 +1235,7 @@ export {
|
|
|
980
1235
|
validateActivity,
|
|
981
1236
|
validateDraft,
|
|
982
1237
|
validateItemGroup,
|
|
1238
|
+
validateItemGroupDraft,
|
|
983
1239
|
validateXAPIStatement,
|
|
984
1240
|
verifyAttemptPlan,
|
|
985
1241
|
writtenResponseJsonSchema,
|