@lessonkit/lxpack 1.4.0 → 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/README.md CHANGED
@@ -4,9 +4,15 @@
4
4
  [![Documentation](https://readthedocs.org/projects/lessonkit/badge/?version=latest)](https://lessonkit.readthedocs.io/en/latest/reference/packaging.html)
5
5
  [![License](https://img.shields.io/github/license/eddiethedean/lessonkit)](https://github.com/eddiethedean/lessonkit/blob/main/LICENSE)
6
6
 
7
- Package Vite SPAs for LMS delivery — SCORM 1.2/2004, standalone, xAPI, and cmi5. `@lessonkit/lxpack` bundles [`@lxpack/*`](https://www.npmjs.com/org/lxpack) as direct dependencies (no separate `@lxpack/api` install).
7
+ Package Vite SPAs for LMS delivery — SCORM 1.2/2004, standalone, xAPI, and cmi5. Bundles [`@lxpack/*`](https://www.npmjs.com/org/lxpack) as direct dependencies.
8
8
 
9
- Requires Node.js **18+**.
9
+ ## When to install
10
+
11
+ - Custom packaging pipelines without the CLI
12
+ - Validating `lessonkit.json` / course descriptors in CI
13
+ - Programmatic LMS export from your own build tools
14
+
15
+ Most authors use `lessonkit package` (CLI) which calls this package internally.
10
16
 
11
17
  ## Install
12
18
 
@@ -14,13 +20,18 @@ Requires Node.js **18+**.
14
20
  npm install @lessonkit/lxpack
15
21
  ```
16
22
 
23
+ Requires Node.js **18+** minimum; **20.19+** recommended for CLI scaffold workflows.
24
+
17
25
  ## Usage
18
26
 
19
27
  ```typescript
20
- import { packageLessonkitCourse } from "@lessonkit/lxpack";
28
+ import { packageLessonkitCourse, parseLessonkitManifest } from "@lessonkit/lxpack";
29
+
30
+ const manifest = parseLessonkitManifest(await readFile("lessonkit.json", "utf8"));
31
+ if (!manifest.ok) throw manifest.error;
21
32
 
22
33
  const result = await packageLessonkitCourse({
23
- descriptor: courseDescriptor,
34
+ descriptor: manifest.value,
24
35
  outDir: ".lxpack/course",
25
36
  spaDistDir: "dist",
26
37
  target: "scorm12",
@@ -30,19 +41,36 @@ const result = await packageLessonkitCourse({
30
41
  if (!result.ok) throw new Error("packaging failed");
31
42
  ```
32
43
 
33
- Prefer the CLI: `lessonkit package --target scorm12` reads `lessonkit.json` and runs the same pipeline.
44
+ Prefer the CLI: `lessonkit package --target scorm12` reads `lessonkit.json` and runs the same staged pipeline.
45
+
46
+ ## Layouts
47
+
48
+ | Layout | Use case |
49
+ | --- | --- |
50
+ | `single-spa` | One Vite SPA for the whole course (CLI default) |
51
+ | `per-lesson-spa` | One dist per lesson (advanced; see packaging reference) |
34
52
 
35
53
  ## Browser bridge
36
54
 
37
- When embedded in an LXPack iframe, `@lessonkit/react` forwards completion events to `window.parent.lxpackBridge.v1`. Direct API:
55
+ When embedded in an LXPack iframe, `@lessonkit/react` forwards completion events to `window.parent.lxpackBridge.v1`:
38
56
 
39
57
  ```typescript
40
58
  import { forwardTelemetryToBridge } from "@lessonkit/lxpack/bridge";
41
59
  ```
42
60
 
61
+ Production builds require `allowedParentOrigins` when `bridge: "auto"`.
62
+
63
+ ## Common issues
64
+
65
+ | Symptom | Fix |
66
+ | --- | --- |
67
+ | React/manifest ID mismatch | Run strict parity validation; align IDs in `App.tsx` and `lessonkit.json` |
68
+ | xAPI/cmi5 validation failure | Set HTTPS `activityIri` in manifest |
69
+ | Empty `dist/` | Run `lessonkit build` before `package` (or omit `--no-build`) |
70
+
43
71
  ## Docs
44
72
 
45
- [Packaging reference](https://lessonkit.readthedocs.io/en/latest/reference/packaging.html) · [LXPack bridge](https://lessonkit.readthedocs.io/en/latest/reference/lxpack-bridge.html) · [Golden example](https://github.com/eddiethedean/lessonkit/tree/main/examples/lxpack-golden)
73
+ [Packaging reference](https://lessonkit.readthedocs.io/en/latest/reference/packaging.html) · [LXPack bridge](https://lessonkit.readthedocs.io/en/latest/reference/lxpack-bridge.html) · [Manifest](https://lessonkit.readthedocs.io/en/latest/reference/manifest.html) · [Golden example](https://github.com/eddiethedean/lessonkit/tree/main/examples/lxpack-golden) · [TypeDoc API index](https://lessonkit.readthedocs.io/en/latest/reference/api.html)
46
74
 
47
75
  ## License
48
76
 
package/dist/bridge.cjs CHANGED
@@ -20,14 +20,17 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/bridge.ts
21
21
  var bridge_exports = {};
22
22
  __export(bridge_exports, {
23
+ BRANCH_TELEMETRY_EVENTS: () => BRANCH_TELEMETRY_EVENTS,
23
24
  DEFAULT_BRIDGE_PASSING_SCORE: () => import_spa_bridge2.DEFAULT_BRIDGE_PASSING_SCORE,
24
25
  LESSONKIT_TELEMETRY_EVENTS: () => import_tracking_schema2.LESSONKIT_TELEMETRY_EVENTS,
25
26
  LXPACK_BRIDGE_VERSIONS: () => import_spa_bridge2.LXPACK_BRIDGE_VERSIONS,
27
+ branchTelemetryToBridgeTrackEvent: () => branchTelemetryToBridgeTrackEvent,
26
28
  createLxpackBridge: () => createLxpackBridge,
27
29
  createLxpackBridgeHost: () => import_spa_bridge2.createLxpackBridgeHost,
28
30
  dispatchBridgeAction: () => dispatchBridgeAction,
29
31
  forwardTelemetryToBridge: () => forwardTelemetryToBridge,
30
- getLxpackBridge: () => import_spa_bridge2.getLxpackBridge,
32
+ getLxpackBridge: () => getLxpackBridge,
33
+ isParentOriginAllowed: () => isParentOriginAllowed,
31
34
  mapLessonkitTelemetryToBridgeAction: () => import_tracking_schema2.mapLessonkitTelemetryToBridgeAction,
32
35
  mapLessonkitTelemetryToLxpack: () => import_tracking_schema2.mapLessonkitTelemetryToLxpack,
33
36
  normalizeAssessmentPassingScore: () => normalizeAssessmentPassingScore,
@@ -37,6 +40,7 @@ __export(bridge_exports, {
37
40
  notifyLxpackAssessment: () => notifyLxpackAssessment,
38
41
  notifyLxpackCourseComplete: () => notifyLxpackCourseComplete,
39
42
  notifyLxpackLessonComplete: () => notifyLxpackLessonComplete,
43
+ resolveParentOrigin: () => resolveParentOrigin,
40
44
  supportedBridgeVersions: () => import_spa_bridge2.supportedBridgeVersions,
41
45
  telemetryEventToLessonkit: () => telemetryEventToLessonkit
42
46
  });
@@ -48,43 +52,91 @@ var import_tracking_schema3 = require("@lxpack/tracking-schema");
48
52
 
49
53
  // src/telemetry.ts
50
54
  var import_tracking_schema = require("@lxpack/tracking-schema");
51
- var SUPPORTED = new Set(import_tracking_schema.LESSONKIT_TELEMETRY_EVENTS);
55
+ var BRANCH_TELEMETRY_EVENTS = ["branch_node_viewed", "branch_selected"];
56
+ var ASSESSMENT_TELEMETRY_EVENTS = ["assessment_answered"];
57
+ var SUPPORTED = /* @__PURE__ */ new Set([
58
+ ...import_tracking_schema.LESSONKIT_TELEMETRY_EVENTS,
59
+ ...BRANCH_TELEMETRY_EVENTS,
60
+ ...ASSESSMENT_TELEMETRY_EVENTS
61
+ ]);
52
62
  function isQuizAnsweredData(data) {
53
- return typeof data === "object" && data !== null && typeof data.checkId === "string";
63
+ return typeof data === "object" && data !== null && typeof data.checkId === "string" && data.checkId.length > 0;
54
64
  }
55
65
  function isQuizCompletedData(data) {
56
- return typeof data === "object" && data !== null && typeof data.checkId === "string";
66
+ return typeof data === "object" && data !== null && typeof data.checkId === "string" && data.checkId.length > 0;
67
+ }
68
+ function isAssessmentAnsweredData(data) {
69
+ return typeof data === "object" && data !== null && typeof data.checkId === "string" && data.checkId.length > 0;
57
70
  }
58
71
  function isInteractionData(data) {
59
72
  return typeof data === "object" && data !== null;
60
73
  }
74
+ function isBranchNodeViewedData(data) {
75
+ return typeof data === "object" && data !== null && typeof data.blockId === "string" && typeof data.nodeId === "string";
76
+ }
77
+ function isBranchSelectedData(data) {
78
+ return typeof data === "object" && data !== null && typeof data.blockId === "string" && typeof data.fromNodeId === "string" && typeof data.toNodeId === "string";
79
+ }
61
80
  function telemetryEventToLessonkit(event) {
62
81
  if (!SUPPORTED.has(event.name)) {
63
82
  return null;
64
83
  }
65
- const name = event.name;
66
84
  const mapped = {
67
- name,
85
+ name: event.name,
68
86
  lessonId: event.lessonId
69
87
  };
70
- if (name === "quiz_completed" || name === "quiz_answered") {
88
+ if (event.name === "quiz_completed" || event.name === "quiz_answered" || event.name === "assessment_answered") {
71
89
  const data = event.data;
72
- if (isQuizAnsweredData(data) || isQuizCompletedData(data)) {
73
- mapped.assessmentId = data.checkId;
74
- if ("score" in data) {
75
- mapped.score = data.score;
76
- mapped.maxScore = data.maxScore;
77
- mapped.passingScore = data.passingScore;
78
- }
79
- mapped.data = data;
90
+ if (!isQuizAnsweredData(data) && !isQuizCompletedData(data) && !isAssessmentAnsweredData(data)) {
91
+ return null;
80
92
  }
81
- } else if (name === "interaction" && event.data && isInteractionData(event.data)) {
93
+ mapped.assessmentId = data.checkId;
94
+ if ("score" in data) {
95
+ mapped.score = data.score;
96
+ mapped.maxScore = data.maxScore;
97
+ mapped.passingScore = data.passingScore;
98
+ }
99
+ mapped.data = data;
100
+ } else if (mapped.name === "interaction" && event.data && isInteractionData(event.data)) {
101
+ mapped.data = event.data;
102
+ } else if (event.name === "branch_node_viewed" && isBranchNodeViewedData(event.data)) {
103
+ mapped.data = event.data;
104
+ } else if (event.name === "branch_selected" && isBranchSelectedData(event.data)) {
82
105
  mapped.data = event.data;
83
106
  }
84
107
  return mapped;
85
108
  }
109
+ function answeredTelemetryToBridgeTrackEvent(event) {
110
+ if (event.name !== "quiz_answered" && event.name !== "assessment_answered") {
111
+ return null;
112
+ }
113
+ const lessonkitEvent = telemetryEventToLessonkit(event);
114
+ if (!lessonkitEvent?.assessmentId) return null;
115
+ return (0, import_tracking_schema.mapLessonkitTelemetryToLxpack)({
116
+ ...lessonkitEvent,
117
+ name: "quiz_answered"
118
+ });
119
+ }
120
+ function branchTelemetryToBridgeTrackEvent(event) {
121
+ if (event.name === "branch_node_viewed" && isBranchNodeViewedData(event.data)) {
122
+ return {
123
+ type: "interaction",
124
+ id: "branch_node_viewed",
125
+ data: { ...event.data, lessonkitEvent: event.name }
126
+ };
127
+ }
128
+ if (event.name === "branch_selected" && isBranchSelectedData(event.data)) {
129
+ return {
130
+ type: "interaction",
131
+ id: "branch_selected",
132
+ data: { ...event.data, lessonkitEvent: event.name }
133
+ };
134
+ }
135
+ return null;
136
+ }
86
137
 
87
138
  // src/bridge.ts
139
+ var import_meta = {};
88
140
  function normalizeAssessmentScore(opts) {
89
141
  if (typeof opts.score !== "number" || !Number.isFinite(opts.score)) {
90
142
  return null;
@@ -97,7 +149,41 @@ function normalizeAssessmentPassingScore(opts) {
97
149
  maxScore: opts?.maxScore
98
150
  });
99
151
  }
100
- function getBridge(parentWindow) {
152
+ function resolveParentOrigin(parentWindow) {
153
+ if (typeof window === "undefined") return null;
154
+ const parent = parentWindow ?? window.parent;
155
+ if (!parent || parent === window) return null;
156
+ try {
157
+ return parent.location.origin;
158
+ } catch {
159
+ const referrer = typeof document !== "undefined" ? document.referrer : "";
160
+ if (!referrer) return null;
161
+ try {
162
+ return new URL(referrer).origin;
163
+ } catch {
164
+ return null;
165
+ }
166
+ }
167
+ }
168
+ function isProductionRuntime() {
169
+ try {
170
+ if (import_meta.env?.PROD === true) return true;
171
+ } catch {
172
+ }
173
+ const g = globalThis;
174
+ return typeof g.process !== "undefined" && g.process.env?.NODE_ENV === "production";
175
+ }
176
+ function isParentOriginAllowed(allowedParentOrigins, parentWindow, mode) {
177
+ if (mode === "off") return false;
178
+ if (isProductionRuntime() && !allowedParentOrigins?.length) return false;
179
+ if (!allowedParentOrigins?.length) return true;
180
+ const origin = resolveParentOrigin(parentWindow);
181
+ if (!origin) return false;
182
+ return allowedParentOrigins.includes(origin);
183
+ }
184
+ function getBridge(parentWindow, opts) {
185
+ const mode = opts?.mode ?? "auto";
186
+ if (!isParentOriginAllowed(opts?.allowedParentOrigins, parentWindow, mode)) return null;
101
187
  const fromSdk = (0, import_spa_bridge.getLxpackBridge)(parentWindow);
102
188
  if (fromSdk) return fromSdk;
103
189
  if (typeof window === "undefined") return null;
@@ -105,21 +191,33 @@ function getBridge(parentWindow) {
105
191
  if (!parent || parent === window) return null;
106
192
  return parent.lxpackBridge?.v1 ?? parent.lxpack ?? null;
107
193
  }
194
+ function getLxpackBridge(parentWindow, opts) {
195
+ return getBridge(parentWindow, opts);
196
+ }
108
197
  function isDevEnvironment() {
198
+ try {
199
+ if (import_meta.env?.DEV === true) return true;
200
+ if (import_meta.env?.PROD === true) return false;
201
+ } catch {
202
+ }
109
203
  const g = globalThis;
110
204
  return typeof g.process !== "undefined" && g.process.env?.NODE_ENV !== "production";
111
205
  }
112
- function dispatchBridgeAction(bridge, action) {
206
+ function handleBridgeError(err, onBridgeError) {
207
+ onBridgeError?.(err);
208
+ if (isDevEnvironment()) {
209
+ console.warn(
210
+ "[lessonkit/lxpack] lxpack bridge action failed:",
211
+ err instanceof Error ? err.message : err
212
+ );
213
+ }
214
+ }
215
+ function dispatchBridgeAction(bridge, action, opts) {
113
216
  if (!action) return;
114
217
  try {
115
218
  dispatchBridgeActionInner(bridge, action);
116
219
  } catch (err) {
117
- if (isDevEnvironment()) {
118
- console.warn(
119
- "[lessonkit/lxpack] lxpack bridge action failed:",
120
- err instanceof Error ? err.message : err
121
- );
122
- }
220
+ handleBridgeError(err, opts?.onBridgeError);
123
221
  }
124
222
  }
125
223
  function dispatchBridgeActionInner(bridge, action) {
@@ -172,50 +270,70 @@ function forwardAssessmentCompletedToBridge(bridge, event) {
172
270
  maxScore: data.maxScore
173
271
  });
174
272
  }
175
- function forwardTelemetryToBridge(event, mode = "auto", parentWindow) {
273
+ function forwardTelemetryToBridge(event, mode = "auto", parentWindow, opts) {
176
274
  if (mode === "off") return;
177
- const bridge = getBridge(parentWindow);
275
+ const bridge = getBridge(parentWindow, {
276
+ allowedParentOrigins: opts?.allowedParentOrigins,
277
+ mode
278
+ });
178
279
  if (!bridge) return;
179
- if (event.name === "assessment_completed") {
180
- forwardAssessmentCompletedToBridge(bridge, event);
181
- return;
280
+ try {
281
+ if (event.name === "assessment_completed") {
282
+ forwardAssessmentCompletedToBridge(bridge, event);
283
+ return;
284
+ }
285
+ const answeredTrack = answeredTelemetryToBridgeTrackEvent(event);
286
+ if (answeredTrack) {
287
+ bridge.track?.(answeredTrack);
288
+ return;
289
+ }
290
+ const branchTrack = branchTelemetryToBridgeTrackEvent(event);
291
+ if (branchTrack) {
292
+ bridge.track?.(branchTrack);
293
+ return;
294
+ }
295
+ const lessonkitEvent = telemetryEventToLessonkit(event);
296
+ if (!lessonkitEvent) return;
297
+ const action = (0, import_tracking_schema3.mapLessonkitTelemetryToBridgeAction)(lessonkitEvent);
298
+ dispatchBridgeActionInner(bridge, action);
299
+ } catch (err) {
300
+ handleBridgeError(err, opts?.onBridgeError);
182
301
  }
183
- const lessonkitEvent = telemetryEventToLessonkit(event);
184
- if (!lessonkitEvent) return;
185
- const action = (0, import_tracking_schema3.mapLessonkitTelemetryToBridgeAction)(lessonkitEvent);
186
- dispatchBridgeAction(bridge, action);
187
302
  }
188
- function createLxpackBridge() {
189
- return getBridge();
303
+ function createLxpackBridge(opts) {
304
+ return getBridge(void 0, opts);
190
305
  }
191
- function notifyLxpackLessonComplete(lessonId) {
192
- const bridge = getBridge();
306
+ function notifyLxpackLessonComplete(lessonId, opts) {
307
+ const bridge = getBridge(void 0, opts);
193
308
  if (!bridge?.completeLesson) return false;
194
309
  bridge.completeLesson(lessonId);
195
310
  return true;
196
311
  }
197
- function notifyLxpackCourseComplete() {
198
- const bridge = getBridge();
312
+ function notifyLxpackCourseComplete(opts) {
313
+ const bridge = getBridge(void 0, opts);
199
314
  if (!bridge?.completeCourse) return false;
200
315
  bridge.completeCourse();
201
316
  return true;
202
317
  }
203
- function notifyLxpackAssessment(payload) {
204
- const bridge = getBridge();
318
+ function notifyLxpackAssessment(payload, opts) {
319
+ const bridge = getBridge(void 0, opts);
205
320
  if (!bridge?.submitAssessment) return false;
206
321
  bridge.submitAssessment(payload);
207
322
  return true;
208
323
  }
209
324
  // Annotate the CommonJS export names for ESM import in node:
210
325
  0 && (module.exports = {
326
+ BRANCH_TELEMETRY_EVENTS,
211
327
  DEFAULT_BRIDGE_PASSING_SCORE,
212
328
  LESSONKIT_TELEMETRY_EVENTS,
213
329
  LXPACK_BRIDGE_VERSIONS,
330
+ branchTelemetryToBridgeTrackEvent,
214
331
  createLxpackBridge,
215
332
  createLxpackBridgeHost,
216
333
  dispatchBridgeAction,
217
334
  forwardTelemetryToBridge,
218
335
  getLxpackBridge,
336
+ isParentOriginAllowed,
219
337
  mapLessonkitTelemetryToBridgeAction,
220
338
  mapLessonkitTelemetryToLxpack,
221
339
  normalizeAssessmentPassingScore,
@@ -225,6 +343,7 @@ function notifyLxpackAssessment(payload) {
225
343
  notifyLxpackAssessment,
226
344
  notifyLxpackCourseComplete,
227
345
  notifyLxpackLessonComplete,
346
+ resolveParentOrigin,
228
347
  supportedBridgeVersions,
229
348
  telemetryEventToLessonkit
230
349
  });
package/dist/bridge.d.cts CHANGED
@@ -1,9 +1,9 @@
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, getLxpackBridge, normalizePassingThreshold, normalizeScore, supportedBridgeVersions } from '@lxpack/spa-bridge';
3
+ export { DEFAULT_BRIDGE_PASSING_SCORE, LXPACK_BRIDGE_VERSIONS, LxpackBridgeSubmitAssessmentPayload, LxpackBridgeV1, createLxpackBridgeHost, normalizePassingThreshold, normalizeScore, 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-gCxlwc7I.cjs';
6
+ export { B as BRANCH_TELEMETRY_EVENTS, b as branchTelemetryToBridgeTrackEvent, t as telemetryEventToLessonkit } from './telemetry-0fIWoomS.cjs';
7
7
 
8
8
  /**
9
9
  * Scale a raw quiz score to 0–1 for the LXPack parent bridge.
@@ -21,20 +21,38 @@ declare function normalizeAssessmentPassingScore(opts?: {
21
21
  passingScore?: number;
22
22
  maxScore?: number;
23
23
  }): number;
24
+ type BridgeAccessOptions = {
25
+ /** Allowed parent-frame origins (scheme + host + port). When set, bridge calls require a matching origin. */
26
+ allowedParentOrigins?: string[];
27
+ /** LMS bridge mode; `"auto"` in production requires `allowedParentOrigins`. */
28
+ mode?: LxpackBridgeMode;
29
+ };
30
+ /** Resolve the parent frame origin when embedded (same-origin parent or document.referrer fallback). */
31
+ declare function resolveParentOrigin(parentWindow?: Window): string | null;
32
+ /** Returns true when no allowlist is configured or the resolved parent origin is listed. */
33
+ declare function isParentOriginAllowed(allowedParentOrigins: string[] | undefined, parentWindow?: Window, mode?: LxpackBridgeMode): boolean;
34
+ /** Resolve the LXPack parent bridge when the parent origin passes validation. */
35
+ declare function getLxpackBridge(parentWindow?: Window, opts?: BridgeAccessOptions): LxpackBridgeV1 | null;
24
36
  /** @deprecated Use `LmsBridgeMode` from `@lessonkit/core`. */
25
37
  type LxpackBridgeMode = LmsBridgeMode;
26
38
  /** Apply a mapped bridge action to an LXPack bridge instance. */
27
- declare function dispatchBridgeAction(bridge: LxpackBridgeV1, action: ReturnType<typeof mapLessonkitTelemetryToBridgeAction>): void;
28
- declare function forwardTelemetryToBridge(event: TelemetryEvent, mode?: LxpackBridgeMode, parentWindow?: Window): void;
29
- declare function createLxpackBridge(): LxpackBridgeV1 | null;
30
- declare function notifyLxpackLessonComplete(lessonId: LessonId): boolean;
31
- declare function notifyLxpackCourseComplete(): boolean;
39
+ declare function dispatchBridgeAction(bridge: LxpackBridgeV1, action: ReturnType<typeof mapLessonkitTelemetryToBridgeAction>, opts?: {
40
+ onBridgeError?: (err: unknown) => void;
41
+ }): void;
42
+ type ForwardTelemetryToBridgeOptions = {
43
+ onBridgeError?: (err: unknown) => void;
44
+ allowedParentOrigins?: string[];
45
+ };
46
+ declare function forwardTelemetryToBridge(event: TelemetryEvent, mode?: LxpackBridgeMode, parentWindow?: Window, opts?: ForwardTelemetryToBridgeOptions): void;
47
+ declare function createLxpackBridge(opts?: BridgeAccessOptions): LxpackBridgeV1 | null;
48
+ declare function notifyLxpackLessonComplete(lessonId: LessonId, opts?: BridgeAccessOptions): boolean;
49
+ declare function notifyLxpackCourseComplete(opts?: BridgeAccessOptions): boolean;
32
50
  /**
33
51
  * Submit assessment results to the parent LXPack bridge.
34
52
  * `score` must already be on a 0–1 scale (use `normalizeAssessmentScore` for raw points).
35
53
  */
36
54
  declare function notifyLxpackAssessment(payload: LxpackBridgeSubmitAssessmentPayload & {
37
55
  id: CheckId;
38
- }): boolean;
56
+ }, opts?: BridgeAccessOptions): boolean;
39
57
 
40
- export { type LxpackBridgeMode, createLxpackBridge, dispatchBridgeAction, forwardTelemetryToBridge, normalizeAssessmentPassingScore, normalizeAssessmentScore, notifyLxpackAssessment, notifyLxpackCourseComplete, notifyLxpackLessonComplete };
58
+ export { type BridgeAccessOptions, type ForwardTelemetryToBridgeOptions, type LxpackBridgeMode, createLxpackBridge, dispatchBridgeAction, forwardTelemetryToBridge, getLxpackBridge, isParentOriginAllowed, normalizeAssessmentPassingScore, normalizeAssessmentScore, notifyLxpackAssessment, notifyLxpackCourseComplete, notifyLxpackLessonComplete, resolveParentOrigin };
package/dist/bridge.d.ts CHANGED
@@ -1,9 +1,9 @@
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, getLxpackBridge, normalizePassingThreshold, normalizeScore, supportedBridgeVersions } from '@lxpack/spa-bridge';
3
+ export { DEFAULT_BRIDGE_PASSING_SCORE, LXPACK_BRIDGE_VERSIONS, LxpackBridgeSubmitAssessmentPayload, LxpackBridgeV1, createLxpackBridgeHost, normalizePassingThreshold, normalizeScore, 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-gCxlwc7I.js';
6
+ export { B as BRANCH_TELEMETRY_EVENTS, b as branchTelemetryToBridgeTrackEvent, t as telemetryEventToLessonkit } from './telemetry-0fIWoomS.js';
7
7
 
8
8
  /**
9
9
  * Scale a raw quiz score to 0–1 for the LXPack parent bridge.
@@ -21,20 +21,38 @@ declare function normalizeAssessmentPassingScore(opts?: {
21
21
  passingScore?: number;
22
22
  maxScore?: number;
23
23
  }): number;
24
+ type BridgeAccessOptions = {
25
+ /** Allowed parent-frame origins (scheme + host + port). When set, bridge calls require a matching origin. */
26
+ allowedParentOrigins?: string[];
27
+ /** LMS bridge mode; `"auto"` in production requires `allowedParentOrigins`. */
28
+ mode?: LxpackBridgeMode;
29
+ };
30
+ /** Resolve the parent frame origin when embedded (same-origin parent or document.referrer fallback). */
31
+ declare function resolveParentOrigin(parentWindow?: Window): string | null;
32
+ /** Returns true when no allowlist is configured or the resolved parent origin is listed. */
33
+ declare function isParentOriginAllowed(allowedParentOrigins: string[] | undefined, parentWindow?: Window, mode?: LxpackBridgeMode): boolean;
34
+ /** Resolve the LXPack parent bridge when the parent origin passes validation. */
35
+ declare function getLxpackBridge(parentWindow?: Window, opts?: BridgeAccessOptions): LxpackBridgeV1 | null;
24
36
  /** @deprecated Use `LmsBridgeMode` from `@lessonkit/core`. */
25
37
  type LxpackBridgeMode = LmsBridgeMode;
26
38
  /** Apply a mapped bridge action to an LXPack bridge instance. */
27
- declare function dispatchBridgeAction(bridge: LxpackBridgeV1, action: ReturnType<typeof mapLessonkitTelemetryToBridgeAction>): void;
28
- declare function forwardTelemetryToBridge(event: TelemetryEvent, mode?: LxpackBridgeMode, parentWindow?: Window): void;
29
- declare function createLxpackBridge(): LxpackBridgeV1 | null;
30
- declare function notifyLxpackLessonComplete(lessonId: LessonId): boolean;
31
- declare function notifyLxpackCourseComplete(): boolean;
39
+ declare function dispatchBridgeAction(bridge: LxpackBridgeV1, action: ReturnType<typeof mapLessonkitTelemetryToBridgeAction>, opts?: {
40
+ onBridgeError?: (err: unknown) => void;
41
+ }): void;
42
+ type ForwardTelemetryToBridgeOptions = {
43
+ onBridgeError?: (err: unknown) => void;
44
+ allowedParentOrigins?: string[];
45
+ };
46
+ declare function forwardTelemetryToBridge(event: TelemetryEvent, mode?: LxpackBridgeMode, parentWindow?: Window, opts?: ForwardTelemetryToBridgeOptions): void;
47
+ declare function createLxpackBridge(opts?: BridgeAccessOptions): LxpackBridgeV1 | null;
48
+ declare function notifyLxpackLessonComplete(lessonId: LessonId, opts?: BridgeAccessOptions): boolean;
49
+ declare function notifyLxpackCourseComplete(opts?: BridgeAccessOptions): boolean;
32
50
  /**
33
51
  * Submit assessment results to the parent LXPack bridge.
34
52
  * `score` must already be on a 0–1 scale (use `normalizeAssessmentScore` for raw points).
35
53
  */
36
54
  declare function notifyLxpackAssessment(payload: LxpackBridgeSubmitAssessmentPayload & {
37
55
  id: CheckId;
38
- }): boolean;
56
+ }, opts?: BridgeAccessOptions): boolean;
39
57
 
40
- export { type LxpackBridgeMode, createLxpackBridge, dispatchBridgeAction, forwardTelemetryToBridge, normalizeAssessmentPassingScore, normalizeAssessmentScore, notifyLxpackAssessment, notifyLxpackCourseComplete, notifyLxpackLessonComplete };
58
+ export { type BridgeAccessOptions, type ForwardTelemetryToBridgeOptions, type LxpackBridgeMode, createLxpackBridge, dispatchBridgeAction, forwardTelemetryToBridge, getLxpackBridge, isParentOriginAllowed, normalizeAssessmentPassingScore, normalizeAssessmentScore, notifyLxpackAssessment, notifyLxpackCourseComplete, notifyLxpackLessonComplete, resolveParentOrigin };