@lessonkit/lxpack 1.4.0 → 1.6.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/README.md +49 -7
- package/block-tree.v1.json +40 -0
- package/dist/bridge.cjs +229 -55
- package/dist/bridge.d.cts +59 -11
- package/dist/bridge.d.ts +59 -11
- package/dist/bridge.js +162 -42
- package/dist/chunk-HTZR4CF3.js +94 -0
- package/dist/index.cjs +1527 -209
- package/dist/index.d.cts +184 -5
- package/dist/index.d.ts +184 -5
- package/dist/index.js +1472 -183
- package/dist/telemetry-0fIWoomS.d.cts +17 -0
- package/dist/telemetry-0fIWoomS.d.ts +17 -0
- package/lessonkit-manifest.v1.json +6 -3
- package/lkcourse-format.v1.json +21 -0
- package/package.json +14 -8
- package/dist/chunk-DYQI222N.js +0 -41
- package/dist/telemetry-gCxlwc7I.d.cts +0 -9
- package/dist/telemetry-gCxlwc7I.d.ts +0 -9
package/dist/bridge.d.ts
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import { LmsBridgeMode, TelemetryEvent, CheckId, LessonId } from '@lessonkit/core';
|
|
2
2
|
import { LxpackBridgeV1, LxpackBridgeSubmitAssessmentPayload } from '@lxpack/spa-bridge';
|
|
3
|
-
export { DEFAULT_BRIDGE_PASSING_SCORE, LXPACK_BRIDGE_VERSIONS, LxpackBridgeSubmitAssessmentPayload, LxpackBridgeV1, createLxpackBridgeHost,
|
|
3
|
+
export { DEFAULT_BRIDGE_PASSING_SCORE, LXPACK_BRIDGE_VERSIONS, LxpackBridgeSubmitAssessmentPayload, LxpackBridgeV1, createLxpackBridgeHost, supportedBridgeVersions } from '@lxpack/spa-bridge';
|
|
4
4
|
import { mapLessonkitTelemetryToBridgeAction } from '@lxpack/tracking-schema';
|
|
5
5
|
export { LESSONKIT_TELEMETRY_EVENTS, LessonkitBridgeAction, LessonkitTelemetryEvent, LessonkitTelemetryEventName, TrackingSchemaEvent, mapLessonkitTelemetryToBridgeAction, mapLessonkitTelemetryToLxpack } from '@lxpack/tracking-schema';
|
|
6
|
-
export { t as telemetryEventToLessonkit } from './telemetry-
|
|
6
|
+
export { B as BRANCH_TELEMETRY_EVENTS, b as branchTelemetryToBridgeTrackEvent, t as telemetryEventToLessonkit } from './telemetry-0fIWoomS.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Scale a raw quiz score to 0–1 for the LXPack parent bridge.
|
|
10
|
+
* When `maxScore > 1`, always treats `score` as raw points (fixes partial-credit 1/N cases).
|
|
11
|
+
*/
|
|
12
|
+
declare function normalizeScore(raw: {
|
|
13
|
+
score?: number;
|
|
14
|
+
maxScore?: number;
|
|
15
|
+
}): number | null;
|
|
16
|
+
/** Scale a raw passing threshold to 0–1 for the LXPack parent bridge. */
|
|
17
|
+
declare function normalizePassingThreshold(raw?: {
|
|
18
|
+
passingScore?: number;
|
|
19
|
+
maxScore?: number;
|
|
20
|
+
}): number;
|
|
7
21
|
|
|
8
22
|
/**
|
|
9
23
|
* Scale a raw quiz score to 0–1 for the LXPack parent bridge.
|
|
@@ -15,26 +29,60 @@ declare function normalizeAssessmentScore(opts: {
|
|
|
15
29
|
}): number | null;
|
|
16
30
|
/**
|
|
17
31
|
* Scale a raw passing threshold to 0–1 for the LXPack parent bridge.
|
|
18
|
-
*
|
|
32
|
+
* Default 1.0 (100%) when omitted — matches React SPA default.
|
|
19
33
|
*/
|
|
20
34
|
declare function normalizeAssessmentPassingScore(opts?: {
|
|
21
35
|
passingScore?: number;
|
|
22
36
|
maxScore?: number;
|
|
23
37
|
}): number;
|
|
38
|
+
type BridgeAccessOptions = {
|
|
39
|
+
/** Allowed parent-frame origins (scheme + host + port). When set, bridge calls require a matching origin. */
|
|
40
|
+
allowedParentOrigins?: string[];
|
|
41
|
+
/** LMS bridge mode; `"auto"` in production requires `allowedParentOrigins`. */
|
|
42
|
+
mode?: LxpackBridgeMode;
|
|
43
|
+
onBridgeError?: (err: unknown) => void;
|
|
44
|
+
};
|
|
45
|
+
/** Resolve the parent frame origin when embedded (same-origin parent or document.referrer fallback). */
|
|
46
|
+
declare function resolveParentOrigin(parentWindow?: Window): string | null;
|
|
47
|
+
/** Returns true when no allowlist is configured or the resolved parent origin is listed. */
|
|
48
|
+
declare function isParentOriginAllowed(allowedParentOrigins: string[] | undefined, parentWindow?: Window, mode?: LxpackBridgeMode): boolean;
|
|
49
|
+
/** Resolve the LXPack parent bridge when the parent origin passes validation. */
|
|
50
|
+
declare function getLxpackBridge(parentWindow?: Window, opts?: BridgeAccessOptions): LxpackBridgeV1 | null;
|
|
24
51
|
/** @deprecated Use `LmsBridgeMode` from `@lessonkit/core`. */
|
|
25
52
|
type LxpackBridgeMode = LmsBridgeMode;
|
|
26
53
|
/** Apply a mapped bridge action to an LXPack bridge instance. */
|
|
27
|
-
declare function dispatchBridgeAction(bridge: LxpackBridgeV1, action: ReturnType<typeof mapLessonkitTelemetryToBridgeAction
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
54
|
+
declare function dispatchBridgeAction(bridge: LxpackBridgeV1, action: ReturnType<typeof mapLessonkitTelemetryToBridgeAction>, opts?: {
|
|
55
|
+
onBridgeError?: (err: unknown) => void;
|
|
56
|
+
}): void;
|
|
57
|
+
type ForwardTelemetryToBridgeOptions = {
|
|
58
|
+
onBridgeError?: (err: unknown) => void;
|
|
59
|
+
/** Called when assessment_completed cannot be forwarded (e.g. missing/invalid score). */
|
|
60
|
+
onBridgeMiss?: (event: TelemetryEvent) => void;
|
|
61
|
+
allowedParentOrigins?: string[];
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Map a LessonKit telemetry event to LMS bridge actions (`track`, `complete`, assessment score).
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* import { forwardTelemetryToBridge } from "@lessonkit/lxpack/bridge";
|
|
69
|
+
*
|
|
70
|
+
* forwardTelemetryToBridge(event, "auto", window.parent, {
|
|
71
|
+
* allowedParentOrigins: ["https://lms.example.com"],
|
|
72
|
+
* onBridgeMiss: (e) => console.warn("bridge miss", e.name),
|
|
73
|
+
* });
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
declare function forwardTelemetryToBridge(event: TelemetryEvent, mode?: LxpackBridgeMode, parentWindow?: Window, opts?: ForwardTelemetryToBridgeOptions): void;
|
|
77
|
+
declare function createLxpackBridge(opts?: BridgeAccessOptions): LxpackBridgeV1 | null;
|
|
78
|
+
declare function notifyLxpackLessonComplete(lessonId: LessonId, opts?: BridgeAccessOptions): boolean;
|
|
79
|
+
declare function notifyLxpackCourseComplete(opts?: BridgeAccessOptions): boolean;
|
|
32
80
|
/**
|
|
33
81
|
* Submit assessment results to the parent LXPack bridge.
|
|
34
|
-
*
|
|
82
|
+
* Raw point scores are normalized to 0–1 before submission.
|
|
35
83
|
*/
|
|
36
84
|
declare function notifyLxpackAssessment(payload: LxpackBridgeSubmitAssessmentPayload & {
|
|
37
85
|
id: CheckId;
|
|
38
|
-
}): boolean;
|
|
86
|
+
}, opts?: BridgeAccessOptions): boolean;
|
|
39
87
|
|
|
40
|
-
export { type LxpackBridgeMode, createLxpackBridge, dispatchBridgeAction, forwardTelemetryToBridge, normalizeAssessmentPassingScore, normalizeAssessmentScore, notifyLxpackAssessment, notifyLxpackCourseComplete, notifyLxpackLessonComplete };
|
|
88
|
+
export { type BridgeAccessOptions, type ForwardTelemetryToBridgeOptions, type LxpackBridgeMode, createLxpackBridge, dispatchBridgeAction, forwardTelemetryToBridge, getLxpackBridge, isParentOriginAllowed, normalizeAssessmentPassingScore, normalizeAssessmentScore, normalizePassingThreshold, normalizeScore, notifyLxpackAssessment, notifyLxpackCourseComplete, notifyLxpackLessonComplete, resolveParentOrigin };
|
package/dist/bridge.js
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
+
BRANCH_TELEMETRY_EVENTS,
|
|
3
|
+
answeredTelemetryToBridgeTrackEvent,
|
|
4
|
+
branchTelemetryToBridgeTrackEvent,
|
|
2
5
|
telemetryEventToLessonkit
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-HTZR4CF3.js";
|
|
4
7
|
|
|
5
8
|
// src/bridge.ts
|
|
6
9
|
import {
|
|
7
|
-
getLxpackBridge as getLxpackBridgeFromParent
|
|
8
|
-
normalizePassingThreshold,
|
|
9
|
-
normalizeScore
|
|
10
|
+
getLxpackBridge as getLxpackBridgeFromParent
|
|
10
11
|
} from "@lxpack/spa-bridge";
|
|
11
12
|
import {
|
|
12
13
|
createLxpackBridgeHost,
|
|
13
14
|
DEFAULT_BRIDGE_PASSING_SCORE,
|
|
14
|
-
getLxpackBridge,
|
|
15
15
|
LXPACK_BRIDGE_VERSIONS,
|
|
16
|
-
normalizePassingThreshold as normalizePassingThreshold2,
|
|
17
|
-
normalizeScore as normalizeScore2,
|
|
18
16
|
supportedBridgeVersions
|
|
19
17
|
} from "@lxpack/spa-bridge";
|
|
20
18
|
import {
|
|
@@ -23,6 +21,37 @@ import {
|
|
|
23
21
|
mapLessonkitTelemetryToLxpack
|
|
24
22
|
} from "@lxpack/tracking-schema";
|
|
25
23
|
import { mapLessonkitTelemetryToBridgeAction as mapLessonkitTelemetryToBridgeAction2 } from "@lxpack/tracking-schema";
|
|
24
|
+
var DEFAULT_BRIDGE_PASSING_SCORE2 = 1;
|
|
25
|
+
function clamp01(value) {
|
|
26
|
+
return Math.min(1, Math.max(0, value));
|
|
27
|
+
}
|
|
28
|
+
function normalizeScore(raw) {
|
|
29
|
+
const { score, maxScore } = raw;
|
|
30
|
+
if (typeof score !== "number" || !Number.isFinite(score)) return null;
|
|
31
|
+
if (typeof maxScore === "number" && maxScore > 0) {
|
|
32
|
+
return clamp01(score / maxScore);
|
|
33
|
+
}
|
|
34
|
+
if (score > 1 && score <= 100) {
|
|
35
|
+
return clamp01(score / 100);
|
|
36
|
+
}
|
|
37
|
+
return clamp01(score);
|
|
38
|
+
}
|
|
39
|
+
function normalizePassingThreshold(raw) {
|
|
40
|
+
const { passingScore, maxScore } = raw ?? {};
|
|
41
|
+
if (typeof passingScore !== "number" || !Number.isFinite(passingScore)) {
|
|
42
|
+
return DEFAULT_BRIDGE_PASSING_SCORE2;
|
|
43
|
+
}
|
|
44
|
+
if (typeof maxScore === "number" && maxScore > 1) {
|
|
45
|
+
return clamp01(passingScore / maxScore);
|
|
46
|
+
}
|
|
47
|
+
if (typeof maxScore === "number" && maxScore <= 1) {
|
|
48
|
+
return clamp01(passingScore);
|
|
49
|
+
}
|
|
50
|
+
if (passingScore > 1 && passingScore <= 100) {
|
|
51
|
+
return clamp01(passingScore / 100);
|
|
52
|
+
}
|
|
53
|
+
return clamp01(passingScore);
|
|
54
|
+
}
|
|
26
55
|
function normalizeAssessmentScore(opts) {
|
|
27
56
|
if (typeof opts.score !== "number" || !Number.isFinite(opts.score)) {
|
|
28
57
|
return null;
|
|
@@ -35,7 +64,35 @@ function normalizeAssessmentPassingScore(opts) {
|
|
|
35
64
|
maxScore: opts?.maxScore
|
|
36
65
|
});
|
|
37
66
|
}
|
|
38
|
-
function
|
|
67
|
+
function resolveParentOrigin(parentWindow) {
|
|
68
|
+
if (typeof window === "undefined") return null;
|
|
69
|
+
const parent = parentWindow ?? window.parent;
|
|
70
|
+
if (!parent || parent === window) return null;
|
|
71
|
+
try {
|
|
72
|
+
return parent.location.origin;
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function isProductionRuntime() {
|
|
78
|
+
try {
|
|
79
|
+
if (import.meta.env?.PROD === true) return true;
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
const g = globalThis;
|
|
83
|
+
return typeof g.process !== "undefined" && g.process.env?.NODE_ENV === "production";
|
|
84
|
+
}
|
|
85
|
+
function isParentOriginAllowed(allowedParentOrigins, parentWindow, mode) {
|
|
86
|
+
if (mode === "off") return false;
|
|
87
|
+
if (isProductionRuntime() && !allowedParentOrigins?.length) return false;
|
|
88
|
+
if (!allowedParentOrigins?.length) return true;
|
|
89
|
+
const origin = resolveParentOrigin(parentWindow);
|
|
90
|
+
if (!origin) return false;
|
|
91
|
+
return allowedParentOrigins.includes(origin);
|
|
92
|
+
}
|
|
93
|
+
function getBridge(parentWindow, opts) {
|
|
94
|
+
const mode = opts?.mode ?? "auto";
|
|
95
|
+
if (!isParentOriginAllowed(opts?.allowedParentOrigins, parentWindow, mode)) return null;
|
|
39
96
|
const fromSdk = getLxpackBridgeFromParent(parentWindow);
|
|
40
97
|
if (fromSdk) return fromSdk;
|
|
41
98
|
if (typeof window === "undefined") return null;
|
|
@@ -43,21 +100,33 @@ function getBridge(parentWindow) {
|
|
|
43
100
|
if (!parent || parent === window) return null;
|
|
44
101
|
return parent.lxpackBridge?.v1 ?? parent.lxpack ?? null;
|
|
45
102
|
}
|
|
103
|
+
function getLxpackBridge(parentWindow, opts) {
|
|
104
|
+
return getBridge(parentWindow, opts);
|
|
105
|
+
}
|
|
46
106
|
function isDevEnvironment() {
|
|
107
|
+
try {
|
|
108
|
+
if (import.meta.env?.DEV === true) return true;
|
|
109
|
+
if (import.meta.env?.PROD === true) return false;
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
47
112
|
const g = globalThis;
|
|
48
113
|
return typeof g.process !== "undefined" && g.process.env?.NODE_ENV !== "production";
|
|
49
114
|
}
|
|
50
|
-
function
|
|
115
|
+
function handleBridgeError(err, onBridgeError) {
|
|
116
|
+
onBridgeError?.(err);
|
|
117
|
+
if (isDevEnvironment()) {
|
|
118
|
+
console.warn(
|
|
119
|
+
"[lessonkit/lxpack] lxpack bridge action failed:",
|
|
120
|
+
err instanceof Error ? err.message : err
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function dispatchBridgeAction(bridge, action, opts) {
|
|
51
125
|
if (!action) return;
|
|
52
126
|
try {
|
|
53
127
|
dispatchBridgeActionInner(bridge, action);
|
|
54
128
|
} catch (err) {
|
|
55
|
-
|
|
56
|
-
console.warn(
|
|
57
|
-
"[lessonkit/lxpack] lxpack bridge action failed:",
|
|
58
|
-
err instanceof Error ? err.message : err
|
|
59
|
-
);
|
|
60
|
-
}
|
|
129
|
+
handleBridgeError(err, opts?.onBridgeError);
|
|
61
130
|
}
|
|
62
131
|
}
|
|
63
132
|
function dispatchBridgeActionInner(bridge, action) {
|
|
@@ -93,13 +162,16 @@ function dispatchBridgeActionInner(bridge, action) {
|
|
|
93
162
|
return;
|
|
94
163
|
}
|
|
95
164
|
}
|
|
96
|
-
function forwardAssessmentCompletedToBridge(bridge, event) {
|
|
165
|
+
function forwardAssessmentCompletedToBridge(bridge, event, onBridgeMiss) {
|
|
97
166
|
const data = event.data;
|
|
98
167
|
const scaled = normalizeAssessmentScore({
|
|
99
168
|
score: data.score,
|
|
100
169
|
maxScore: data.maxScore
|
|
101
170
|
});
|
|
102
|
-
if (scaled === null)
|
|
171
|
+
if (scaled === null) {
|
|
172
|
+
onBridgeMiss?.(event);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
103
175
|
bridge.submitAssessment?.({
|
|
104
176
|
id: data.checkId,
|
|
105
177
|
score: scaled,
|
|
@@ -110,58 +182,106 @@ function forwardAssessmentCompletedToBridge(bridge, event) {
|
|
|
110
182
|
maxScore: data.maxScore
|
|
111
183
|
});
|
|
112
184
|
}
|
|
113
|
-
function forwardTelemetryToBridge(event, mode = "auto", parentWindow) {
|
|
185
|
+
function forwardTelemetryToBridge(event, mode = "auto", parentWindow, opts) {
|
|
114
186
|
if (mode === "off") return;
|
|
115
|
-
const bridge = getBridge(parentWindow
|
|
187
|
+
const bridge = getBridge(parentWindow, {
|
|
188
|
+
allowedParentOrigins: opts?.allowedParentOrigins,
|
|
189
|
+
mode
|
|
190
|
+
});
|
|
116
191
|
if (!bridge) return;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
192
|
+
try {
|
|
193
|
+
if (event.name === "assessment_completed") {
|
|
194
|
+
forwardAssessmentCompletedToBridge(bridge, event, opts?.onBridgeMiss);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const answeredTrack = answeredTelemetryToBridgeTrackEvent(event);
|
|
198
|
+
if (answeredTrack) {
|
|
199
|
+
bridge.track?.(answeredTrack);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const branchTrack = branchTelemetryToBridgeTrackEvent(event);
|
|
203
|
+
if (branchTrack) {
|
|
204
|
+
bridge.track?.(branchTrack);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const lessonkitEvent = telemetryEventToLessonkit(event);
|
|
208
|
+
if (!lessonkitEvent) return;
|
|
209
|
+
const action = mapLessonkitTelemetryToBridgeAction2(lessonkitEvent);
|
|
210
|
+
dispatchBridgeActionInner(bridge, action);
|
|
211
|
+
} catch (err) {
|
|
212
|
+
handleBridgeError(err, opts?.onBridgeError);
|
|
120
213
|
}
|
|
121
|
-
const lessonkitEvent = telemetryEventToLessonkit(event);
|
|
122
|
-
if (!lessonkitEvent) return;
|
|
123
|
-
const action = mapLessonkitTelemetryToBridgeAction2(lessonkitEvent);
|
|
124
|
-
dispatchBridgeAction(bridge, action);
|
|
125
214
|
}
|
|
126
|
-
function createLxpackBridge() {
|
|
127
|
-
return getBridge();
|
|
215
|
+
function createLxpackBridge(opts) {
|
|
216
|
+
return getBridge(void 0, opts);
|
|
128
217
|
}
|
|
129
|
-
function notifyLxpackLessonComplete(lessonId) {
|
|
130
|
-
const bridge = getBridge();
|
|
218
|
+
function notifyLxpackLessonComplete(lessonId, opts) {
|
|
219
|
+
const bridge = getBridge(void 0, opts);
|
|
131
220
|
if (!bridge?.completeLesson) return false;
|
|
132
|
-
|
|
133
|
-
|
|
221
|
+
try {
|
|
222
|
+
bridge.completeLesson(lessonId);
|
|
223
|
+
return true;
|
|
224
|
+
} catch (err) {
|
|
225
|
+
handleBridgeError(err, opts?.onBridgeError);
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
134
228
|
}
|
|
135
|
-
function notifyLxpackCourseComplete() {
|
|
136
|
-
const bridge = getBridge();
|
|
229
|
+
function notifyLxpackCourseComplete(opts) {
|
|
230
|
+
const bridge = getBridge(void 0, opts);
|
|
137
231
|
if (!bridge?.completeCourse) return false;
|
|
138
|
-
|
|
139
|
-
|
|
232
|
+
try {
|
|
233
|
+
bridge.completeCourse();
|
|
234
|
+
return true;
|
|
235
|
+
} catch (err) {
|
|
236
|
+
handleBridgeError(err, opts?.onBridgeError);
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
140
239
|
}
|
|
141
|
-
function notifyLxpackAssessment(payload) {
|
|
142
|
-
const bridge = getBridge();
|
|
240
|
+
function notifyLxpackAssessment(payload, opts) {
|
|
241
|
+
const bridge = getBridge(void 0, opts);
|
|
143
242
|
if (!bridge?.submitAssessment) return false;
|
|
144
|
-
|
|
145
|
-
|
|
243
|
+
const scaled = normalizeAssessmentScore({
|
|
244
|
+
score: payload.score,
|
|
245
|
+
maxScore: payload.maxScore
|
|
246
|
+
});
|
|
247
|
+
if (scaled === null) return false;
|
|
248
|
+
try {
|
|
249
|
+
bridge.submitAssessment({
|
|
250
|
+
...payload,
|
|
251
|
+
score: scaled,
|
|
252
|
+
passingScore: normalizeAssessmentPassingScore({
|
|
253
|
+
passingScore: payload.passingScore,
|
|
254
|
+
maxScore: payload.maxScore
|
|
255
|
+
})
|
|
256
|
+
});
|
|
257
|
+
return true;
|
|
258
|
+
} catch (err) {
|
|
259
|
+
handleBridgeError(err, opts?.onBridgeError);
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
146
262
|
}
|
|
147
263
|
export {
|
|
264
|
+
BRANCH_TELEMETRY_EVENTS,
|
|
148
265
|
DEFAULT_BRIDGE_PASSING_SCORE,
|
|
149
266
|
LESSONKIT_TELEMETRY_EVENTS,
|
|
150
267
|
LXPACK_BRIDGE_VERSIONS,
|
|
268
|
+
branchTelemetryToBridgeTrackEvent,
|
|
151
269
|
createLxpackBridge,
|
|
152
270
|
createLxpackBridgeHost,
|
|
153
271
|
dispatchBridgeAction,
|
|
154
272
|
forwardTelemetryToBridge,
|
|
155
273
|
getLxpackBridge,
|
|
274
|
+
isParentOriginAllowed,
|
|
156
275
|
mapLessonkitTelemetryToBridgeAction,
|
|
157
276
|
mapLessonkitTelemetryToLxpack,
|
|
158
277
|
normalizeAssessmentPassingScore,
|
|
159
278
|
normalizeAssessmentScore,
|
|
160
|
-
|
|
161
|
-
|
|
279
|
+
normalizePassingThreshold,
|
|
280
|
+
normalizeScore,
|
|
162
281
|
notifyLxpackAssessment,
|
|
163
282
|
notifyLxpackCourseComplete,
|
|
164
283
|
notifyLxpackLessonComplete,
|
|
284
|
+
resolveParentOrigin,
|
|
165
285
|
supportedBridgeVersions,
|
|
166
286
|
telemetryEventToLessonkit
|
|
167
287
|
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// src/telemetry.ts
|
|
2
|
+
import {
|
|
3
|
+
LESSONKIT_TELEMETRY_EVENTS,
|
|
4
|
+
mapLessonkitTelemetryToLxpack
|
|
5
|
+
} from "@lxpack/tracking-schema";
|
|
6
|
+
var BRANCH_TELEMETRY_EVENTS = ["branch_node_viewed", "branch_selected"];
|
|
7
|
+
var ASSESSMENT_TELEMETRY_EVENTS = ["assessment_answered"];
|
|
8
|
+
var SUPPORTED = /* @__PURE__ */ new Set([
|
|
9
|
+
...LESSONKIT_TELEMETRY_EVENTS,
|
|
10
|
+
...BRANCH_TELEMETRY_EVENTS,
|
|
11
|
+
...ASSESSMENT_TELEMETRY_EVENTS
|
|
12
|
+
]);
|
|
13
|
+
function isQuizAnsweredData(data) {
|
|
14
|
+
return typeof data === "object" && data !== null && typeof data.checkId === "string" && data.checkId.length > 0;
|
|
15
|
+
}
|
|
16
|
+
function isQuizCompletedData(data) {
|
|
17
|
+
return typeof data === "object" && data !== null && typeof data.checkId === "string" && data.checkId.length > 0;
|
|
18
|
+
}
|
|
19
|
+
function isAssessmentAnsweredData(data) {
|
|
20
|
+
return typeof data === "object" && data !== null && typeof data.checkId === "string" && data.checkId.length > 0;
|
|
21
|
+
}
|
|
22
|
+
function isInteractionData(data) {
|
|
23
|
+
return typeof data === "object" && data !== null;
|
|
24
|
+
}
|
|
25
|
+
function isBranchNodeViewedData(data) {
|
|
26
|
+
return typeof data === "object" && data !== null && typeof data.blockId === "string" && typeof data.nodeId === "string";
|
|
27
|
+
}
|
|
28
|
+
function isBranchSelectedData(data) {
|
|
29
|
+
return typeof data === "object" && data !== null && typeof data.blockId === "string" && typeof data.fromNodeId === "string" && typeof data.toNodeId === "string";
|
|
30
|
+
}
|
|
31
|
+
function telemetryEventToLessonkit(event) {
|
|
32
|
+
if (!SUPPORTED.has(event.name)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const mapped = {
|
|
36
|
+
name: event.name,
|
|
37
|
+
lessonId: event.lessonId
|
|
38
|
+
};
|
|
39
|
+
if (event.name === "quiz_completed" || event.name === "quiz_answered" || event.name === "assessment_answered") {
|
|
40
|
+
const data = event.data;
|
|
41
|
+
if (!isQuizAnsweredData(data) && !isQuizCompletedData(data) && !isAssessmentAnsweredData(data)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
mapped.assessmentId = data.checkId;
|
|
45
|
+
if ("score" in data) {
|
|
46
|
+
mapped.score = data.score;
|
|
47
|
+
mapped.maxScore = data.maxScore;
|
|
48
|
+
mapped.passingScore = data.passingScore;
|
|
49
|
+
}
|
|
50
|
+
mapped.data = data;
|
|
51
|
+
} else if (mapped.name === "interaction" && event.data && isInteractionData(event.data)) {
|
|
52
|
+
mapped.data = event.data;
|
|
53
|
+
} else if (event.name === "branch_node_viewed" && isBranchNodeViewedData(event.data)) {
|
|
54
|
+
mapped.data = event.data;
|
|
55
|
+
} else if (event.name === "branch_selected" && isBranchSelectedData(event.data)) {
|
|
56
|
+
mapped.data = event.data;
|
|
57
|
+
}
|
|
58
|
+
return mapped;
|
|
59
|
+
}
|
|
60
|
+
function answeredTelemetryToBridgeTrackEvent(event) {
|
|
61
|
+
if (event.name !== "quiz_answered" && event.name !== "assessment_answered") {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const lessonkitEvent = telemetryEventToLessonkit(event);
|
|
65
|
+
if (!lessonkitEvent?.assessmentId) return null;
|
|
66
|
+
return mapLessonkitTelemetryToLxpack({
|
|
67
|
+
...lessonkitEvent,
|
|
68
|
+
name: "quiz_answered"
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function branchTelemetryToBridgeTrackEvent(event) {
|
|
72
|
+
if (event.name === "branch_node_viewed" && isBranchNodeViewedData(event.data)) {
|
|
73
|
+
return {
|
|
74
|
+
type: "interaction",
|
|
75
|
+
id: "branch_node_viewed",
|
|
76
|
+
data: { ...event.data, lessonkitEvent: event.name }
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (event.name === "branch_selected" && isBranchSelectedData(event.data)) {
|
|
80
|
+
return {
|
|
81
|
+
type: "interaction",
|
|
82
|
+
id: "branch_selected",
|
|
83
|
+
data: { ...event.data, lessonkitEvent: event.name }
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
BRANCH_TELEMETRY_EVENTS,
|
|
91
|
+
telemetryEventToLessonkit,
|
|
92
|
+
answeredTelemetryToBridgeTrackEvent,
|
|
93
|
+
branchTelemetryToBridgeTrackEvent
|
|
94
|
+
};
|