@lessonkit/lxpack 0.9.1 → 0.9.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 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) {
@@ -447,6 +447,9 @@ async function writeLxpackProject(options) {
447
447
  }
448
448
  const descriptor = validation.descriptor;
449
449
  const outDir = (0, import_node_path4.resolve)(options.outDir);
450
+ if (options.projectRoot) {
451
+ assertResolvedPathUnderRoot((0, import_node_path4.resolve)(options.projectRoot), outDir);
452
+ }
450
453
  const spaDirs = await resolveSpaDirs({ ...options, descriptor });
451
454
  const interchange = descriptorToInterchange(descriptor);
452
455
  const materialized = await (0, import_validators.materializeLessonkitProject)({
@@ -509,7 +512,14 @@ async function promoteStagingToOutDir(stagingDir, outDir) {
509
512
  await fsp.rename(tmpPromote, outDir);
510
513
  } catch (promoteError) {
511
514
  if (hadOutDir) {
512
- await fsp.rename(backup, outDir).catch(() => void 0);
515
+ try {
516
+ await fsp.rename(backup, outDir);
517
+ } catch (restoreError) {
518
+ console.warn(
519
+ `[lessonkit/lxpack] failed to restore ${outDir} after promote error:`,
520
+ restoreError instanceof Error ? restoreError.message : restoreError
521
+ );
522
+ }
513
523
  }
514
524
  await fsp.rm(tmpPromote, { recursive: true, force: true }).catch(() => void 0);
515
525
  throw promoteError;
@@ -521,6 +531,36 @@ async function promoteStagingToOutDir(stagingDir, outDir) {
521
531
  async function packageLessonkitCourse(options) {
522
532
  const { target, output, dir, outputBaseDir, ...writeOpts } = options;
523
533
  const outDir = (0, import_node_path5.resolve)(writeOpts.outDir);
534
+ const projectRoot = writeOpts.projectRoot ? (0, import_node_path5.resolve)(writeOpts.projectRoot) : void 0;
535
+ if (projectRoot) {
536
+ assertResolvedPathUnderRoot(projectRoot, outDir);
537
+ }
538
+ if (outputBaseDir && !isSafeRelativeSpaPath(outputBaseDir)) {
539
+ return {
540
+ ok: false,
541
+ courseDir: outDir,
542
+ target,
543
+ issues: [{ path: "outputBaseDir", message: `unsafe outputBaseDir: ${outputBaseDir}` }]
544
+ };
545
+ }
546
+ if (projectRoot && output) {
547
+ const resolvedOutput = (0, import_node_path5.resolve)(projectRoot, output);
548
+ try {
549
+ assertResolvedPathUnderRoot(projectRoot, resolvedOutput);
550
+ } catch (err) {
551
+ return {
552
+ ok: false,
553
+ courseDir: outDir,
554
+ target,
555
+ issues: [
556
+ {
557
+ path: "output",
558
+ message: err instanceof Error ? err.message : String(err)
559
+ }
560
+ ]
561
+ };
562
+ }
563
+ }
524
564
  const descriptorValidation = validateDescriptor(writeOpts.descriptor);
525
565
  if (!descriptorValidation.ok) {
526
566
  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) {
@@ -396,6 +396,9 @@ async function writeLxpackProject(options) {
396
396
  }
397
397
  const descriptor = validation.descriptor;
398
398
  const outDir = resolve4(options.outDir);
399
+ if (options.projectRoot) {
400
+ assertResolvedPathUnderRoot(resolve4(options.projectRoot), outDir);
401
+ }
399
402
  const spaDirs = await resolveSpaDirs({ ...options, descriptor });
400
403
  const interchange = descriptorToInterchange(descriptor);
401
404
  const materialized = await materializeLessonkitProject({
@@ -462,7 +465,14 @@ async function promoteStagingToOutDir(stagingDir, outDir) {
462
465
  await fsp.rename(tmpPromote, outDir);
463
466
  } catch (promoteError) {
464
467
  if (hadOutDir) {
465
- await fsp.rename(backup, outDir).catch(() => void 0);
468
+ try {
469
+ await fsp.rename(backup, outDir);
470
+ } catch (restoreError) {
471
+ console.warn(
472
+ `[lessonkit/lxpack] failed to restore ${outDir} after promote error:`,
473
+ restoreError instanceof Error ? restoreError.message : restoreError
474
+ );
475
+ }
466
476
  }
467
477
  await fsp.rm(tmpPromote, { recursive: true, force: true }).catch(() => void 0);
468
478
  throw promoteError;
@@ -474,6 +484,36 @@ async function promoteStagingToOutDir(stagingDir, outDir) {
474
484
  async function packageLessonkitCourse(options) {
475
485
  const { target, output, dir, outputBaseDir, ...writeOpts } = options;
476
486
  const outDir = resolve5(writeOpts.outDir);
487
+ const projectRoot = writeOpts.projectRoot ? resolve5(writeOpts.projectRoot) : void 0;
488
+ if (projectRoot) {
489
+ assertResolvedPathUnderRoot(projectRoot, outDir);
490
+ }
491
+ if (outputBaseDir && !isSafeRelativeSpaPath(outputBaseDir)) {
492
+ return {
493
+ ok: false,
494
+ courseDir: outDir,
495
+ target,
496
+ issues: [{ path: "outputBaseDir", message: `unsafe outputBaseDir: ${outputBaseDir}` }]
497
+ };
498
+ }
499
+ if (projectRoot && output) {
500
+ const resolvedOutput = resolve5(projectRoot, output);
501
+ try {
502
+ assertResolvedPathUnderRoot(projectRoot, resolvedOutput);
503
+ } catch (err) {
504
+ return {
505
+ ok: false,
506
+ courseDir: outDir,
507
+ target,
508
+ issues: [
509
+ {
510
+ path: "output",
511
+ message: err instanceof Error ? err.message : String(err)
512
+ }
513
+ ]
514
+ };
515
+ }
516
+ }
477
517
  const descriptorValidation = validateDescriptor(writeOpts.descriptor);
478
518
  if (!descriptorValidation.ok) {
479
519
  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.2",
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.2",
57
+ "@lessonkit/themes": "0.9.2",
58
58
  "@lxpack/api": "^0.6.0",
59
59
  "@lxpack/spa-bridge": "^0.6.0",
60
60
  "@lxpack/tracking-schema": "^0.6.0",