@lessonkit/lxpack 1.3.1 → 1.5.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/dist/bridge.js CHANGED
@@ -1,6 +1,9 @@
1
1
  import {
2
+ BRANCH_TELEMETRY_EVENTS,
3
+ answeredTelemetryToBridgeTrackEvent,
4
+ branchTelemetryToBridgeTrackEvent,
2
5
  telemetryEventToLessonkit
3
- } from "./chunk-DYQI222N.js";
6
+ } from "./chunk-HTZR4CF3.js";
4
7
 
5
8
  // src/bridge.ts
6
9
  import {
@@ -11,7 +14,6 @@ import {
11
14
  import {
12
15
  createLxpackBridgeHost,
13
16
  DEFAULT_BRIDGE_PASSING_SCORE,
14
- getLxpackBridge,
15
17
  LXPACK_BRIDGE_VERSIONS,
16
18
  normalizePassingThreshold as normalizePassingThreshold2,
17
19
  normalizeScore as normalizeScore2,
@@ -35,7 +37,41 @@ function normalizeAssessmentPassingScore(opts) {
35
37
  maxScore: opts?.maxScore
36
38
  });
37
39
  }
38
- function getBridge(parentWindow) {
40
+ function resolveParentOrigin(parentWindow) {
41
+ if (typeof window === "undefined") return null;
42
+ const parent = parentWindow ?? window.parent;
43
+ if (!parent || parent === window) return null;
44
+ try {
45
+ return parent.location.origin;
46
+ } catch {
47
+ const referrer = typeof document !== "undefined" ? document.referrer : "";
48
+ if (!referrer) return null;
49
+ try {
50
+ return new URL(referrer).origin;
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+ }
56
+ function isProductionRuntime() {
57
+ try {
58
+ if (import.meta.env?.PROD === true) return true;
59
+ } catch {
60
+ }
61
+ const g = globalThis;
62
+ return typeof g.process !== "undefined" && g.process.env?.NODE_ENV === "production";
63
+ }
64
+ function isParentOriginAllowed(allowedParentOrigins, parentWindow, mode) {
65
+ if (mode === "off") return false;
66
+ if (isProductionRuntime() && !allowedParentOrigins?.length) return false;
67
+ if (!allowedParentOrigins?.length) return true;
68
+ const origin = resolveParentOrigin(parentWindow);
69
+ if (!origin) return false;
70
+ return allowedParentOrigins.includes(origin);
71
+ }
72
+ function getBridge(parentWindow, opts) {
73
+ const mode = opts?.mode ?? "auto";
74
+ if (!isParentOriginAllowed(opts?.allowedParentOrigins, parentWindow, mode)) return null;
39
75
  const fromSdk = getLxpackBridgeFromParent(parentWindow);
40
76
  if (fromSdk) return fromSdk;
41
77
  if (typeof window === "undefined") return null;
@@ -43,21 +79,33 @@ function getBridge(parentWindow) {
43
79
  if (!parent || parent === window) return null;
44
80
  return parent.lxpackBridge?.v1 ?? parent.lxpack ?? null;
45
81
  }
82
+ function getLxpackBridge(parentWindow, opts) {
83
+ return getBridge(parentWindow, opts);
84
+ }
46
85
  function isDevEnvironment() {
86
+ try {
87
+ if (import.meta.env?.DEV === true) return true;
88
+ if (import.meta.env?.PROD === true) return false;
89
+ } catch {
90
+ }
47
91
  const g = globalThis;
48
92
  return typeof g.process !== "undefined" && g.process.env?.NODE_ENV !== "production";
49
93
  }
50
- function dispatchBridgeAction(bridge, action) {
94
+ function handleBridgeError(err, onBridgeError) {
95
+ onBridgeError?.(err);
96
+ if (isDevEnvironment()) {
97
+ console.warn(
98
+ "[lessonkit/lxpack] lxpack bridge action failed:",
99
+ err instanceof Error ? err.message : err
100
+ );
101
+ }
102
+ }
103
+ function dispatchBridgeAction(bridge, action, opts) {
51
104
  if (!action) return;
52
105
  try {
53
106
  dispatchBridgeActionInner(bridge, action);
54
107
  } catch (err) {
55
- if (isDevEnvironment()) {
56
- console.warn(
57
- "[lessonkit/lxpack] lxpack bridge action failed:",
58
- err instanceof Error ? err.message : err
59
- );
60
- }
108
+ handleBridgeError(err, opts?.onBridgeError);
61
109
  }
62
110
  }
63
111
  function dispatchBridgeActionInner(bridge, action) {
@@ -110,49 +158,69 @@ function forwardAssessmentCompletedToBridge(bridge, event) {
110
158
  maxScore: data.maxScore
111
159
  });
112
160
  }
113
- function forwardTelemetryToBridge(event, mode = "auto", parentWindow) {
161
+ function forwardTelemetryToBridge(event, mode = "auto", parentWindow, opts) {
114
162
  if (mode === "off") return;
115
- const bridge = getBridge(parentWindow);
163
+ const bridge = getBridge(parentWindow, {
164
+ allowedParentOrigins: opts?.allowedParentOrigins,
165
+ mode
166
+ });
116
167
  if (!bridge) return;
117
- if (event.name === "assessment_completed") {
118
- forwardAssessmentCompletedToBridge(bridge, event);
119
- return;
168
+ try {
169
+ if (event.name === "assessment_completed") {
170
+ forwardAssessmentCompletedToBridge(bridge, event);
171
+ return;
172
+ }
173
+ const answeredTrack = answeredTelemetryToBridgeTrackEvent(event);
174
+ if (answeredTrack) {
175
+ bridge.track?.(answeredTrack);
176
+ return;
177
+ }
178
+ const branchTrack = branchTelemetryToBridgeTrackEvent(event);
179
+ if (branchTrack) {
180
+ bridge.track?.(branchTrack);
181
+ return;
182
+ }
183
+ const lessonkitEvent = telemetryEventToLessonkit(event);
184
+ if (!lessonkitEvent) return;
185
+ const action = mapLessonkitTelemetryToBridgeAction2(lessonkitEvent);
186
+ dispatchBridgeActionInner(bridge, action);
187
+ } catch (err) {
188
+ handleBridgeError(err, opts?.onBridgeError);
120
189
  }
121
- const lessonkitEvent = telemetryEventToLessonkit(event);
122
- if (!lessonkitEvent) return;
123
- const action = mapLessonkitTelemetryToBridgeAction2(lessonkitEvent);
124
- dispatchBridgeAction(bridge, action);
125
190
  }
126
- function createLxpackBridge() {
127
- return getBridge();
191
+ function createLxpackBridge(opts) {
192
+ return getBridge(void 0, opts);
128
193
  }
129
- function notifyLxpackLessonComplete(lessonId) {
130
- const bridge = getBridge();
194
+ function notifyLxpackLessonComplete(lessonId, opts) {
195
+ const bridge = getBridge(void 0, opts);
131
196
  if (!bridge?.completeLesson) return false;
132
197
  bridge.completeLesson(lessonId);
133
198
  return true;
134
199
  }
135
- function notifyLxpackCourseComplete() {
136
- const bridge = getBridge();
200
+ function notifyLxpackCourseComplete(opts) {
201
+ const bridge = getBridge(void 0, opts);
137
202
  if (!bridge?.completeCourse) return false;
138
203
  bridge.completeCourse();
139
204
  return true;
140
205
  }
141
- function notifyLxpackAssessment(payload) {
142
- const bridge = getBridge();
206
+ function notifyLxpackAssessment(payload, opts) {
207
+ const bridge = getBridge(void 0, opts);
143
208
  if (!bridge?.submitAssessment) return false;
144
209
  bridge.submitAssessment(payload);
145
210
  return true;
146
211
  }
147
212
  export {
213
+ BRANCH_TELEMETRY_EVENTS,
148
214
  DEFAULT_BRIDGE_PASSING_SCORE,
149
215
  LESSONKIT_TELEMETRY_EVENTS,
150
216
  LXPACK_BRIDGE_VERSIONS,
217
+ branchTelemetryToBridgeTrackEvent,
151
218
  createLxpackBridge,
152
219
  createLxpackBridgeHost,
153
220
  dispatchBridgeAction,
154
221
  forwardTelemetryToBridge,
155
222
  getLxpackBridge,
223
+ isParentOriginAllowed,
156
224
  mapLessonkitTelemetryToBridgeAction,
157
225
  mapLessonkitTelemetryToLxpack,
158
226
  normalizeAssessmentPassingScore,
@@ -162,6 +230,7 @@ export {
162
230
  notifyLxpackAssessment,
163
231
  notifyLxpackCourseComplete,
164
232
  notifyLxpackLessonComplete,
233
+ resolveParentOrigin,
165
234
  supportedBridgeVersions,
166
235
  telemetryEventToLessonkit
167
236
  };
@@ -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
+ };