@lessonkit/xapi 1.0.0 → 1.0.2
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/dist/index.cjs +29 -15
- package/dist/index.d.cts +22 -4
- package/dist/index.d.ts +22 -4
- package/dist/index.js +18 -4
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -59,10 +59,11 @@ function createInMemoryXAPIQueue() {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
// src/client.ts
|
|
62
|
-
var
|
|
62
|
+
var import_core3 = require("@lessonkit/core");
|
|
63
63
|
|
|
64
64
|
// src/telemetryMap.ts
|
|
65
65
|
var import_core = require("@lessonkit/core");
|
|
66
|
+
var import_core2 = require("@lessonkit/core");
|
|
66
67
|
|
|
67
68
|
// src/id.ts
|
|
68
69
|
function cryptoRandomId() {
|
|
@@ -90,13 +91,13 @@ function telemetryEventToXAPIStatement(event) {
|
|
|
90
91
|
const { courseId } = event;
|
|
91
92
|
switch (event.name) {
|
|
92
93
|
case "course_started":
|
|
93
|
-
return statementFor((0,
|
|
94
|
+
return statementFor((0, import_core2.buildLessonkitUrn)({ courseId }), XAPIVerbs.initialized, event.timestamp);
|
|
94
95
|
case "course_completed":
|
|
95
|
-
return statementFor((0,
|
|
96
|
+
return statementFor((0, import_core2.buildLessonkitUrn)({ courseId }), XAPIVerbs.completed, event.timestamp);
|
|
96
97
|
case "lesson_started": {
|
|
97
98
|
const lessonId = event.lessonId;
|
|
98
99
|
return statementFor(
|
|
99
|
-
(0,
|
|
100
|
+
(0, import_core2.buildLessonkitUrn)({ courseId, lessonId }),
|
|
100
101
|
XAPIVerbs.initialized,
|
|
101
102
|
event.timestamp
|
|
102
103
|
);
|
|
@@ -119,7 +120,7 @@ function telemetryEventToXAPIStatement(event) {
|
|
|
119
120
|
scaled: typeof raw === "number" && typeof max === "number" && max > 0 ? raw / max : void 0
|
|
120
121
|
};
|
|
121
122
|
}
|
|
122
|
-
return statementFor((0,
|
|
123
|
+
return statementFor((0, import_core2.buildLessonkitUrn)({ courseId, lessonId }), XAPIVerbs.completed, event.timestamp, {
|
|
123
124
|
result: Object.keys(result).length ? result : void 0
|
|
124
125
|
});
|
|
125
126
|
}
|
|
@@ -133,7 +134,7 @@ function telemetryEventToXAPIStatement(event) {
|
|
|
133
134
|
result.success = event.data.correct;
|
|
134
135
|
}
|
|
135
136
|
return statementFor(
|
|
136
|
-
(0,
|
|
137
|
+
(0, import_core2.buildLessonkitUrn)({ courseId, lessonId, checkId }),
|
|
137
138
|
XAPIVerbs.answered,
|
|
138
139
|
event.timestamp,
|
|
139
140
|
{ result: Object.keys(result).length ? result : void 0 }
|
|
@@ -155,7 +156,7 @@ function telemetryEventToXAPIStatement(event) {
|
|
|
155
156
|
};
|
|
156
157
|
}
|
|
157
158
|
return statementFor(
|
|
158
|
-
(0,
|
|
159
|
+
(0, import_core2.buildLessonkitUrn)({ courseId, lessonId, checkId }),
|
|
159
160
|
XAPIVerbs.completed,
|
|
160
161
|
event.timestamp,
|
|
161
162
|
{ result: Object.keys(result).length ? result : void 0 }
|
|
@@ -166,13 +167,13 @@ function telemetryEventToXAPIStatement(event) {
|
|
|
166
167
|
const blockId = event.data?.blockId;
|
|
167
168
|
if (!lessonId || !blockId) return null;
|
|
168
169
|
return statementFor(
|
|
169
|
-
(0,
|
|
170
|
+
(0, import_core2.buildLessonkitUrn)({ courseId, lessonId, blockId }),
|
|
170
171
|
XAPIVerbs.experienced,
|
|
171
172
|
event.timestamp
|
|
172
173
|
);
|
|
173
174
|
}
|
|
174
175
|
default:
|
|
175
|
-
return
|
|
176
|
+
return (0, import_core.assertNever)(event, "Unhandled telemetry event");
|
|
176
177
|
}
|
|
177
178
|
}
|
|
178
179
|
function statementFor(objectId, verb, timestamp, extra) {
|
|
@@ -210,8 +211,17 @@ function createXAPIClient(opts) {
|
|
|
210
211
|
return;
|
|
211
212
|
}
|
|
212
213
|
const existing = inflightById.get(statement.id);
|
|
213
|
-
if (existing)
|
|
214
|
-
|
|
214
|
+
if (existing) {
|
|
215
|
+
void existing.then(
|
|
216
|
+
() => void 0,
|
|
217
|
+
() => {
|
|
218
|
+
sendOrQueue(statement);
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const transportFlight = Promise.resolve().then(() => transport(statement));
|
|
224
|
+
const flight = transportFlight.catch(() => {
|
|
215
225
|
queue.enqueue(statement);
|
|
216
226
|
if (isDevEnvironment() && !warnedTransportFailure) {
|
|
217
227
|
warnedTransportFailure = true;
|
|
@@ -222,7 +232,7 @@ function createXAPIClient(opts) {
|
|
|
222
232
|
}).finally(() => {
|
|
223
233
|
inflightById.delete(statement.id);
|
|
224
234
|
});
|
|
225
|
-
inflightById.set(statement.id,
|
|
235
|
+
inflightById.set(statement.id, transportFlight);
|
|
226
236
|
void flight;
|
|
227
237
|
};
|
|
228
238
|
const emit = (event) => {
|
|
@@ -237,12 +247,16 @@ function createXAPIClient(opts) {
|
|
|
237
247
|
flush: async () => {
|
|
238
248
|
if (!transport) return;
|
|
239
249
|
await queue.flush(transport);
|
|
250
|
+
const flights = [...inflightById.values()];
|
|
251
|
+
if (flights.length > 0) {
|
|
252
|
+
await Promise.allSettled(flights);
|
|
253
|
+
}
|
|
240
254
|
},
|
|
241
255
|
startedLesson: ({ lessonId }) => {
|
|
242
256
|
if (!courseId) return;
|
|
243
257
|
emit({
|
|
244
258
|
name: "lesson_started",
|
|
245
|
-
timestamp: (0,
|
|
259
|
+
timestamp: (0, import_core3.nowIso)(),
|
|
246
260
|
courseId,
|
|
247
261
|
lessonId,
|
|
248
262
|
data: { lessonId }
|
|
@@ -258,7 +272,7 @@ function createXAPIClient(opts) {
|
|
|
258
272
|
if (!courseId) return;
|
|
259
273
|
emit({
|
|
260
274
|
name: "lesson_completed",
|
|
261
|
-
timestamp: (0,
|
|
275
|
+
timestamp: (0, import_core3.nowIso)(),
|
|
262
276
|
courseId,
|
|
263
277
|
lessonId,
|
|
264
278
|
data: { lessonId, durationMs, score, maxScore, success }
|
|
@@ -268,7 +282,7 @@ function createXAPIClient(opts) {
|
|
|
268
282
|
if (!courseId) return;
|
|
269
283
|
emit({
|
|
270
284
|
name: "course_completed",
|
|
271
|
-
timestamp: (0,
|
|
285
|
+
timestamp: (0, import_core3.nowIso)(),
|
|
272
286
|
courseId
|
|
273
287
|
});
|
|
274
288
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
import { LessonId, CourseId, TelemetryEvent } from '@lessonkit/core';
|
|
2
2
|
|
|
3
|
+
type XAPIVerbIri = "http://adlnet.gov/expapi/verbs/initialized" | "http://adlnet.gov/expapi/verbs/completed" | "http://adlnet.gov/expapi/verbs/answered" | "http://adlnet.gov/expapi/verbs/experienced";
|
|
4
|
+
type XAPIScore = {
|
|
5
|
+
raw?: number;
|
|
6
|
+
max?: number;
|
|
7
|
+
min?: number;
|
|
8
|
+
scaled?: number;
|
|
9
|
+
};
|
|
10
|
+
type XAPIResult = {
|
|
11
|
+
duration?: string;
|
|
12
|
+
success?: boolean;
|
|
13
|
+
score?: XAPIScore;
|
|
14
|
+
completion?: boolean;
|
|
15
|
+
};
|
|
16
|
+
type XAPIObjectDefinition = {
|
|
17
|
+
name?: Record<string, string>;
|
|
18
|
+
description?: Record<string, string>;
|
|
19
|
+
type?: string;
|
|
20
|
+
};
|
|
3
21
|
type XAPIStatement = {
|
|
4
22
|
id: string;
|
|
5
23
|
timestamp: string;
|
|
6
|
-
verb:
|
|
24
|
+
verb: XAPIVerbIri;
|
|
7
25
|
object: {
|
|
8
26
|
id: string;
|
|
9
|
-
definition?:
|
|
27
|
+
definition?: XAPIObjectDefinition;
|
|
10
28
|
};
|
|
11
|
-
result?:
|
|
29
|
+
result?: XAPIResult;
|
|
12
30
|
context?: Record<string, unknown>;
|
|
13
31
|
};
|
|
14
32
|
type XAPITransport = (statement: XAPIStatement) => void | Promise<void>;
|
|
@@ -48,4 +66,4 @@ declare function createXAPIClient(opts?: {
|
|
|
48
66
|
*/
|
|
49
67
|
declare function telemetryEventToXAPIStatement(event: TelemetryEvent): XAPIStatement | null;
|
|
50
68
|
|
|
51
|
-
export { type XAPIClient, type XAPIQueue, type XAPIStatement, type XAPITransport, createInMemoryXAPIQueue, createXAPIClient, telemetryEventToXAPIStatement };
|
|
69
|
+
export { type XAPIClient, type XAPIObjectDefinition, type XAPIQueue, type XAPIResult, type XAPIScore, type XAPIStatement, type XAPITransport, type XAPIVerbIri, createInMemoryXAPIQueue, createXAPIClient, telemetryEventToXAPIStatement };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
import { LessonId, CourseId, TelemetryEvent } from '@lessonkit/core';
|
|
2
2
|
|
|
3
|
+
type XAPIVerbIri = "http://adlnet.gov/expapi/verbs/initialized" | "http://adlnet.gov/expapi/verbs/completed" | "http://adlnet.gov/expapi/verbs/answered" | "http://adlnet.gov/expapi/verbs/experienced";
|
|
4
|
+
type XAPIScore = {
|
|
5
|
+
raw?: number;
|
|
6
|
+
max?: number;
|
|
7
|
+
min?: number;
|
|
8
|
+
scaled?: number;
|
|
9
|
+
};
|
|
10
|
+
type XAPIResult = {
|
|
11
|
+
duration?: string;
|
|
12
|
+
success?: boolean;
|
|
13
|
+
score?: XAPIScore;
|
|
14
|
+
completion?: boolean;
|
|
15
|
+
};
|
|
16
|
+
type XAPIObjectDefinition = {
|
|
17
|
+
name?: Record<string, string>;
|
|
18
|
+
description?: Record<string, string>;
|
|
19
|
+
type?: string;
|
|
20
|
+
};
|
|
3
21
|
type XAPIStatement = {
|
|
4
22
|
id: string;
|
|
5
23
|
timestamp: string;
|
|
6
|
-
verb:
|
|
24
|
+
verb: XAPIVerbIri;
|
|
7
25
|
object: {
|
|
8
26
|
id: string;
|
|
9
|
-
definition?:
|
|
27
|
+
definition?: XAPIObjectDefinition;
|
|
10
28
|
};
|
|
11
|
-
result?:
|
|
29
|
+
result?: XAPIResult;
|
|
12
30
|
context?: Record<string, unknown>;
|
|
13
31
|
};
|
|
14
32
|
type XAPITransport = (statement: XAPIStatement) => void | Promise<void>;
|
|
@@ -48,4 +66,4 @@ declare function createXAPIClient(opts?: {
|
|
|
48
66
|
*/
|
|
49
67
|
declare function telemetryEventToXAPIStatement(event: TelemetryEvent): XAPIStatement | null;
|
|
50
68
|
|
|
51
|
-
export { type XAPIClient, type XAPIQueue, type XAPIStatement, type XAPITransport, createInMemoryXAPIQueue, createXAPIClient, telemetryEventToXAPIStatement };
|
|
69
|
+
export { type XAPIClient, type XAPIObjectDefinition, type XAPIQueue, type XAPIResult, type XAPIScore, type XAPIStatement, type XAPITransport, type XAPIVerbIri, createInMemoryXAPIQueue, createXAPIClient, telemetryEventToXAPIStatement };
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,7 @@ function createInMemoryXAPIQueue() {
|
|
|
34
34
|
import { nowIso } from "@lessonkit/core";
|
|
35
35
|
|
|
36
36
|
// src/telemetryMap.ts
|
|
37
|
+
import { assertNever } from "@lessonkit/core";
|
|
37
38
|
import { buildLessonkitUrn } from "@lessonkit/core";
|
|
38
39
|
|
|
39
40
|
// src/id.ts
|
|
@@ -144,7 +145,7 @@ function telemetryEventToXAPIStatement(event) {
|
|
|
144
145
|
);
|
|
145
146
|
}
|
|
146
147
|
default:
|
|
147
|
-
return
|
|
148
|
+
return assertNever(event, "Unhandled telemetry event");
|
|
148
149
|
}
|
|
149
150
|
}
|
|
150
151
|
function statementFor(objectId, verb, timestamp, extra) {
|
|
@@ -182,8 +183,17 @@ function createXAPIClient(opts) {
|
|
|
182
183
|
return;
|
|
183
184
|
}
|
|
184
185
|
const existing = inflightById.get(statement.id);
|
|
185
|
-
if (existing)
|
|
186
|
-
|
|
186
|
+
if (existing) {
|
|
187
|
+
void existing.then(
|
|
188
|
+
() => void 0,
|
|
189
|
+
() => {
|
|
190
|
+
sendOrQueue(statement);
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const transportFlight = Promise.resolve().then(() => transport(statement));
|
|
196
|
+
const flight = transportFlight.catch(() => {
|
|
187
197
|
queue.enqueue(statement);
|
|
188
198
|
if (isDevEnvironment() && !warnedTransportFailure) {
|
|
189
199
|
warnedTransportFailure = true;
|
|
@@ -194,7 +204,7 @@ function createXAPIClient(opts) {
|
|
|
194
204
|
}).finally(() => {
|
|
195
205
|
inflightById.delete(statement.id);
|
|
196
206
|
});
|
|
197
|
-
inflightById.set(statement.id,
|
|
207
|
+
inflightById.set(statement.id, transportFlight);
|
|
198
208
|
void flight;
|
|
199
209
|
};
|
|
200
210
|
const emit = (event) => {
|
|
@@ -209,6 +219,10 @@ function createXAPIClient(opts) {
|
|
|
209
219
|
flush: async () => {
|
|
210
220
|
if (!transport) return;
|
|
211
221
|
await queue.flush(transport);
|
|
222
|
+
const flights = [...inflightById.values()];
|
|
223
|
+
if (flights.length > 0) {
|
|
224
|
+
await Promise.allSettled(flights);
|
|
225
|
+
}
|
|
212
226
|
},
|
|
213
227
|
startedLesson: ({ lessonId }) => {
|
|
214
228
|
if (!courseId) return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lessonkit/xapi",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "xAPI statement generation primitives for LessonKit.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
"lint": "echo \"(no lint configured yet)\""
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@lessonkit/core": "1.0.
|
|
51
|
+
"@lessonkit/core": "1.0.2"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"tsup": "^8.5.0",
|
|
55
55
|
"typescript": "^5.8.3",
|
|
56
|
-
"vitest": "^
|
|
56
|
+
"vitest": "^4.1.8"
|
|
57
57
|
}
|
|
58
58
|
}
|