@lessonkit/lxpack 0.9.1 → 0.9.3

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 CHANGED
@@ -63,7 +63,7 @@ function isSafeRelativeSpaPath(spaPath) {
63
63
  if (spaPath.startsWith("/") || spaPath.startsWith("\\")) return false;
64
64
  if (/^[a-zA-Z]:[/\\]/.test(spaPath)) return false;
65
65
  const segments = spaPath.split(/[/\\]/).filter((s) => s.length > 0);
66
- if (segments.some((s) => s === ".." || s === ".")) return false;
66
+ if (segments.some((s) => s === "..")) return false;
67
67
  return true;
68
68
  }
69
69
  function assertResolvedPathUnderRoot(root, target) {
@@ -432,7 +432,11 @@ async function resolveSpaDirs(options) {
432
432
  if (!src) {
433
433
  throw new Error(`lessonSpaDirs missing build output for lesson "${lesson.id}"`);
434
434
  }
435
- dirs[lesson.id] = (0, import_node_path3.resolve)(src);
435
+ const resolved = projectRoot ? (0, import_node_path3.resolve)(projectRoot, src) : (0, import_node_path3.resolve)(src);
436
+ if (projectRoot) {
437
+ assertResolvedPathUnderRoot((0, import_node_path3.resolve)(projectRoot), resolved);
438
+ }
439
+ dirs[lesson.id] = resolved;
436
440
  }
437
441
  return dirs;
438
442
  }
@@ -447,6 +451,9 @@ async function writeLxpackProject(options) {
447
451
  }
448
452
  const descriptor = validation.descriptor;
449
453
  const outDir = (0, import_node_path4.resolve)(options.outDir);
454
+ if (options.projectRoot) {
455
+ assertResolvedPathUnderRoot((0, import_node_path4.resolve)(options.projectRoot), outDir);
456
+ }
450
457
  const spaDirs = await resolveSpaDirs({ ...options, descriptor });
451
458
  const interchange = descriptorToInterchange(descriptor);
452
459
  const materialized = await (0, import_validators.materializeLessonkitProject)({
@@ -509,7 +516,14 @@ async function promoteStagingToOutDir(stagingDir, outDir) {
509
516
  await fsp.rename(tmpPromote, outDir);
510
517
  } catch (promoteError) {
511
518
  if (hadOutDir) {
512
- await fsp.rename(backup, outDir).catch(() => void 0);
519
+ try {
520
+ await fsp.rename(backup, outDir);
521
+ } catch (restoreError) {
522
+ console.warn(
523
+ `[lessonkit/lxpack] failed to restore ${outDir} after promote error:`,
524
+ restoreError instanceof Error ? restoreError.message : restoreError
525
+ );
526
+ }
513
527
  }
514
528
  await fsp.rm(tmpPromote, { recursive: true, force: true }).catch(() => void 0);
515
529
  throw promoteError;
@@ -521,6 +535,36 @@ async function promoteStagingToOutDir(stagingDir, outDir) {
521
535
  async function packageLessonkitCourse(options) {
522
536
  const { target, output, dir, outputBaseDir, ...writeOpts } = options;
523
537
  const outDir = (0, import_node_path5.resolve)(writeOpts.outDir);
538
+ const projectRoot = writeOpts.projectRoot ? (0, import_node_path5.resolve)(writeOpts.projectRoot) : void 0;
539
+ if (projectRoot) {
540
+ assertResolvedPathUnderRoot(projectRoot, outDir);
541
+ }
542
+ if (outputBaseDir && !isSafeRelativeSpaPath(outputBaseDir)) {
543
+ return {
544
+ ok: false,
545
+ courseDir: outDir,
546
+ target,
547
+ issues: [{ path: "outputBaseDir", message: `unsafe outputBaseDir: ${outputBaseDir}` }]
548
+ };
549
+ }
550
+ if (projectRoot && output) {
551
+ const resolvedOutput = (0, import_node_path5.resolve)(projectRoot, output);
552
+ try {
553
+ assertResolvedPathUnderRoot(projectRoot, resolvedOutput);
554
+ } catch (err) {
555
+ return {
556
+ ok: false,
557
+ courseDir: outDir,
558
+ target,
559
+ issues: [
560
+ {
561
+ path: "output",
562
+ message: err instanceof Error ? err.message : String(err)
563
+ }
564
+ ]
565
+ };
566
+ }
567
+ }
524
568
  const descriptorValidation = validateDescriptor(writeOpts.descriptor);
525
569
  if (!descriptorValidation.ok) {
526
570
  return {
package/dist/index.d.cts CHANGED
@@ -36,6 +36,10 @@ type LessonkitCourseDescriptor = {
36
36
  completion?: {
37
37
  threshold?: number;
38
38
  };
39
+ /** Required for xAPI / cmi5 export targets (`activityIri` on the course activity). */
40
+ xapi?: {
41
+ activityIri?: string;
42
+ };
39
43
  };
40
44
  /** Source Vite `dist` directory for `single-spa` (default: `dist`). */
41
45
  spaDistDir?: string;
package/dist/index.d.ts CHANGED
@@ -36,6 +36,10 @@ type LessonkitCourseDescriptor = {
36
36
  completion?: {
37
37
  threshold?: number;
38
38
  };
39
+ /** Required for xAPI / cmi5 export targets (`activityIri` on the course activity). */
40
+ xapi?: {
41
+ activityIri?: string;
42
+ };
39
43
  };
40
44
  /** Source Vite `dist` directory for `single-spa` (default: `dist`). */
41
45
  spaDistDir?: string;
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ function isSafeRelativeSpaPath(spaPath) {
12
12
  if (spaPath.startsWith("/") || spaPath.startsWith("\\")) return false;
13
13
  if (/^[a-zA-Z]:[/\\]/.test(spaPath)) return false;
14
14
  const segments = spaPath.split(/[/\\]/).filter((s) => s.length > 0);
15
- if (segments.some((s) => s === ".." || s === ".")) return false;
15
+ if (segments.some((s) => s === "..")) return false;
16
16
  return true;
17
17
  }
18
18
  function assertResolvedPathUnderRoot(root, target) {
@@ -381,7 +381,11 @@ async function resolveSpaDirs(options) {
381
381
  if (!src) {
382
382
  throw new Error(`lessonSpaDirs missing build output for lesson "${lesson.id}"`);
383
383
  }
384
- dirs[lesson.id] = resolve3(src);
384
+ const resolved = projectRoot ? resolve3(projectRoot, src) : resolve3(src);
385
+ if (projectRoot) {
386
+ assertResolvedPathUnderRoot(resolve3(projectRoot), resolved);
387
+ }
388
+ dirs[lesson.id] = resolved;
385
389
  }
386
390
  return dirs;
387
391
  }
@@ -396,6 +400,9 @@ async function writeLxpackProject(options) {
396
400
  }
397
401
  const descriptor = validation.descriptor;
398
402
  const outDir = resolve4(options.outDir);
403
+ if (options.projectRoot) {
404
+ assertResolvedPathUnderRoot(resolve4(options.projectRoot), outDir);
405
+ }
399
406
  const spaDirs = await resolveSpaDirs({ ...options, descriptor });
400
407
  const interchange = descriptorToInterchange(descriptor);
401
408
  const materialized = await materializeLessonkitProject({
@@ -462,7 +469,14 @@ async function promoteStagingToOutDir(stagingDir, outDir) {
462
469
  await fsp.rename(tmpPromote, outDir);
463
470
  } catch (promoteError) {
464
471
  if (hadOutDir) {
465
- await fsp.rename(backup, outDir).catch(() => void 0);
472
+ try {
473
+ await fsp.rename(backup, outDir);
474
+ } catch (restoreError) {
475
+ console.warn(
476
+ `[lessonkit/lxpack] failed to restore ${outDir} after promote error:`,
477
+ restoreError instanceof Error ? restoreError.message : restoreError
478
+ );
479
+ }
466
480
  }
467
481
  await fsp.rm(tmpPromote, { recursive: true, force: true }).catch(() => void 0);
468
482
  throw promoteError;
@@ -474,6 +488,36 @@ async function promoteStagingToOutDir(stagingDir, outDir) {
474
488
  async function packageLessonkitCourse(options) {
475
489
  const { target, output, dir, outputBaseDir, ...writeOpts } = options;
476
490
  const outDir = resolve5(writeOpts.outDir);
491
+ const projectRoot = writeOpts.projectRoot ? resolve5(writeOpts.projectRoot) : void 0;
492
+ if (projectRoot) {
493
+ assertResolvedPathUnderRoot(projectRoot, outDir);
494
+ }
495
+ if (outputBaseDir && !isSafeRelativeSpaPath(outputBaseDir)) {
496
+ return {
497
+ ok: false,
498
+ courseDir: outDir,
499
+ target,
500
+ issues: [{ path: "outputBaseDir", message: `unsafe outputBaseDir: ${outputBaseDir}` }]
501
+ };
502
+ }
503
+ if (projectRoot && output) {
504
+ const resolvedOutput = resolve5(projectRoot, output);
505
+ try {
506
+ assertResolvedPathUnderRoot(projectRoot, resolvedOutput);
507
+ } catch (err) {
508
+ return {
509
+ ok: false,
510
+ courseDir: outDir,
511
+ target,
512
+ issues: [
513
+ {
514
+ path: "output",
515
+ message: err instanceof Error ? err.message : String(err)
516
+ }
517
+ ]
518
+ };
519
+ }
520
+ }
477
521
  const descriptorValidation = validateDescriptor(writeOpts.descriptor);
478
522
  if (!descriptorValidation.ok) {
479
523
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lessonkit/lxpack",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "private": false,
5
5
  "description": "LXPack export adapter for LessonKit courses (SCORM, standalone, xAPI, cmi5).",
6
6
  "license": "Apache-2.0",
@@ -53,8 +53,8 @@
53
53
  "lint": "echo \"(no lint configured yet)\""
54
54
  },
55
55
  "dependencies": {
56
- "@lessonkit/core": "0.9.1",
57
- "@lessonkit/themes": "0.9.1",
56
+ "@lessonkit/core": "0.9.3",
57
+ "@lessonkit/themes": "0.9.3",
58
58
  "@lxpack/api": "^0.6.0",
59
59
  "@lxpack/spa-bridge": "^0.6.0",
60
60
  "@lxpack/tracking-schema": "^0.6.0",