@timeback/core 0.2.1-beta.20260313023436 → 0.2.1-beta.20260314020510
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/README.md +8 -0
- package/dist/qti.d.ts +11 -0
- package/dist/qti.d.ts.map +1 -0
- package/dist/qti.js +126 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -31,6 +31,8 @@ const analytics = await timeback.edubridge.analytics.summary()
|
|
|
31
31
|
|
|
32
32
|
// Caliper - learning analytics events
|
|
33
33
|
await timeback.caliper.events.send(event)
|
|
34
|
+
|
|
35
|
+
// Also available: case, clr, masterytrack, powerpath, qti, webhooks
|
|
34
36
|
```
|
|
35
37
|
|
|
36
38
|
## Managing Multiple Clients
|
|
@@ -194,6 +196,12 @@ For standalone usage, install individual packages:
|
|
|
194
196
|
bun add @timeback/oneroster
|
|
195
197
|
bun add @timeback/edubridge
|
|
196
198
|
bun add @timeback/caliper
|
|
199
|
+
bun add @timeback/case
|
|
200
|
+
bun add @timeback/clr
|
|
201
|
+
bun add @timeback/masterytrack
|
|
202
|
+
bun add @timeback/powerpath
|
|
203
|
+
bun add @timeback/qti
|
|
204
|
+
bun add @timeback/webhooks
|
|
197
205
|
```
|
|
198
206
|
|
|
199
207
|
```typescript
|
package/dist/qti.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QTI XML parsing utilities, re-exported from `@timeback/qti/parse`.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* import { parseQtiXml, extractPrompt } from '@timeback/core/qti'
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
export { extractChoices, extractCorrectResponse, extractFeedback, extractInteractionAttributes, extractModalFeedback, extractPrompt, extractResponseDeclaration, parseQtiXml, } from '@timeback/qti/parse';
|
|
10
|
+
export type { ParsedQtiItem, QtiChoice, QtiInlineFeedback, QtiInteractionAttributes, QtiModalFeedback, QtiResponseDeclaration, } from '@timeback/qti/parse';
|
|
11
|
+
//# sourceMappingURL=qti.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qti.d.ts","sourceRoot":"","sources":["../src/qti.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACN,cAAc,EACd,sBAAsB,EACtB,eAAe,EACf,4BAA4B,EAC5B,oBAAoB,EACpB,aAAa,EACb,0BAA0B,EAC1B,WAAW,GACX,MAAM,qBAAqB,CAAA;AAE5B,YAAY,EACX,aAAa,EACb,SAAS,EACT,iBAAiB,EACjB,wBAAwB,EACxB,gBAAgB,EAChB,sBAAsB,GACtB,MAAM,qBAAqB,CAAA"}
|
package/dist/qti.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import"./chunk-3j7jywnx.js";
|
|
2
|
+
|
|
3
|
+
// ../qti/src/parse.ts
|
|
4
|
+
function extractPrompt(xml) {
|
|
5
|
+
if (!xml)
|
|
6
|
+
return "";
|
|
7
|
+
const match = xml.match(/<qti-prompt[^>]*>([\s\S]*?)<\/qti-prompt>/i);
|
|
8
|
+
return match?.[1]?.trim() ?? "";
|
|
9
|
+
}
|
|
10
|
+
function extractChoices(xml) {
|
|
11
|
+
if (!xml)
|
|
12
|
+
return [];
|
|
13
|
+
const choices = [];
|
|
14
|
+
const regex = /<qti-simple-choice[^>]*identifier="([^"]+)"[^>]*>([\s\S]*?)<\/qti-simple-choice>/gi;
|
|
15
|
+
let match;
|
|
16
|
+
while ((match = regex.exec(xml)) !== null) {
|
|
17
|
+
let text = match[2] ?? "";
|
|
18
|
+
text = text.replace(/<qti-feedback-inline[\s\S]*?<\/qti-feedback-inline>/gi, "").trim();
|
|
19
|
+
choices.push({ identifier: match[1] ?? "", text });
|
|
20
|
+
}
|
|
21
|
+
return choices;
|
|
22
|
+
}
|
|
23
|
+
function extractCorrectResponse(xml) {
|
|
24
|
+
if (!xml)
|
|
25
|
+
return null;
|
|
26
|
+
const match = xml.match(/<qti-correct-response>\s*<qti-value>([^<]+)<\/qti-value>/i);
|
|
27
|
+
return match?.[1]?.trim() ?? null;
|
|
28
|
+
}
|
|
29
|
+
function extractFeedback(xml) {
|
|
30
|
+
if (!xml)
|
|
31
|
+
return [];
|
|
32
|
+
const feedback = [];
|
|
33
|
+
const regex = /<qti-feedback-inline([^>]*)>([\s\S]*?)<\/qti-feedback-inline>/gi;
|
|
34
|
+
let match;
|
|
35
|
+
while ((match = regex.exec(xml)) !== null) {
|
|
36
|
+
const attrs = match[1] ?? "";
|
|
37
|
+
const outcomeId = attrs.match(/outcome-identifier="([^"]*)"/);
|
|
38
|
+
const id = attrs.match(/(?:^|\s)identifier="([^"]*)"/);
|
|
39
|
+
const showHide = attrs.match(/show-hide="([^"]*)"/);
|
|
40
|
+
feedback.push({
|
|
41
|
+
outcomeIdentifier: outcomeId?.[1] ?? "",
|
|
42
|
+
identifier: id?.[1] ?? "",
|
|
43
|
+
showHide: showHide?.[1] ?? "show",
|
|
44
|
+
content: (match[2] ?? "").trim()
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return feedback;
|
|
48
|
+
}
|
|
49
|
+
function extractModalFeedback(xml) {
|
|
50
|
+
if (!xml)
|
|
51
|
+
return [];
|
|
52
|
+
const modalFeedback = [];
|
|
53
|
+
const regex = /<qti-modal-feedback([^>]*)>([\s\S]*?)<\/qti-modal-feedback>/gi;
|
|
54
|
+
let match;
|
|
55
|
+
while ((match = regex.exec(xml)) !== null) {
|
|
56
|
+
const attrs = match[1] ?? "";
|
|
57
|
+
const outcomeId = attrs.match(/outcome-identifier="([^"]*)"/);
|
|
58
|
+
const id = attrs.match(/(?:^|\s)identifier="([^"]*)"/);
|
|
59
|
+
const showHide = attrs.match(/show-hide="([^"]*)"/);
|
|
60
|
+
let content = match[2] ?? "";
|
|
61
|
+
const bodyMatch = content.match(/<qti-content-body>([\s\S]*?)<\/qti-content-body>/i);
|
|
62
|
+
if (bodyMatch) {
|
|
63
|
+
content = bodyMatch[1] ?? "";
|
|
64
|
+
}
|
|
65
|
+
modalFeedback.push({
|
|
66
|
+
outcomeIdentifier: outcomeId?.[1] ?? "",
|
|
67
|
+
identifier: id?.[1] ?? "",
|
|
68
|
+
showHide: showHide?.[1] ?? "show",
|
|
69
|
+
content: content.trim()
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return modalFeedback;
|
|
73
|
+
}
|
|
74
|
+
function extractInteractionAttributes(xml) {
|
|
75
|
+
if (!xml)
|
|
76
|
+
return null;
|
|
77
|
+
const match = xml.match(/<qti-choice-interaction([^>]*)>/i);
|
|
78
|
+
if (!match)
|
|
79
|
+
return null;
|
|
80
|
+
const attrs = match[1] ?? "";
|
|
81
|
+
const responseId = attrs.match(/response-identifier="([^"]*)"/);
|
|
82
|
+
const shuffle = attrs.match(/shuffle="([^"]*)"/);
|
|
83
|
+
const maxChoices = attrs.match(/max-choices="([^"]*)"/);
|
|
84
|
+
return {
|
|
85
|
+
responseIdentifier: responseId?.[1] ?? "",
|
|
86
|
+
shuffle: shuffle?.[1]?.toLowerCase() === "true",
|
|
87
|
+
maxChoices: maxChoices?.[1] ? parseInt(maxChoices[1], 10) : 1
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function extractResponseDeclaration(xml) {
|
|
91
|
+
if (!xml)
|
|
92
|
+
return null;
|
|
93
|
+
const match = xml.match(/<qti-response-declaration([^>]*)>/i);
|
|
94
|
+
if (!match)
|
|
95
|
+
return null;
|
|
96
|
+
const attrs = match[1] ?? "";
|
|
97
|
+
const identifier = attrs.match(/identifier="([^"]*)"/);
|
|
98
|
+
const cardinality = attrs.match(/cardinality="([^"]*)"/);
|
|
99
|
+
const baseType = attrs.match(/base-type="([^"]*)"/);
|
|
100
|
+
return {
|
|
101
|
+
identifier: identifier?.[1] ?? "",
|
|
102
|
+
cardinality: cardinality?.[1] ?? "",
|
|
103
|
+
baseType: baseType?.[1] ?? null
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function parseQtiXml(xml) {
|
|
107
|
+
return {
|
|
108
|
+
prompt: extractPrompt(xml),
|
|
109
|
+
choices: extractChoices(xml),
|
|
110
|
+
correctAnswer: extractCorrectResponse(xml),
|
|
111
|
+
feedback: extractFeedback(xml),
|
|
112
|
+
modalFeedback: extractModalFeedback(xml),
|
|
113
|
+
interaction: extractInteractionAttributes(xml),
|
|
114
|
+
responseDeclaration: extractResponseDeclaration(xml)
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export {
|
|
118
|
+
parseQtiXml,
|
|
119
|
+
extractResponseDeclaration,
|
|
120
|
+
extractPrompt,
|
|
121
|
+
extractModalFeedback,
|
|
122
|
+
extractInteractionAttributes,
|
|
123
|
+
extractFeedback,
|
|
124
|
+
extractCorrectResponse,
|
|
125
|
+
extractChoices
|
|
126
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timeback/core",
|
|
3
|
-
"version": "0.2.1-beta.
|
|
3
|
+
"version": "0.2.1-beta.20260314020510",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"./utils": {
|
|
19
19
|
"types": "./dist/utils.d.ts",
|
|
20
20
|
"import": "./dist/utils.js"
|
|
21
|
+
},
|
|
22
|
+
"./qti": {
|
|
23
|
+
"types": "./dist/qti.d.ts",
|
|
24
|
+
"import": "./dist/qti.js"
|
|
21
25
|
}
|
|
22
26
|
},
|
|
23
27
|
"main": "dist/index.js",
|