@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 +47 -3
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +47 -3
- package/package.json +3 -3
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 === ".."
|
|
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
|
-
|
|
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
|
-
|
|
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 === ".."
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
57
|
-
"@lessonkit/themes": "0.9.
|
|
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",
|