@lessonkit/lxpack 0.6.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/index.cjs ADDED
@@ -0,0 +1,577 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ assessmentDescriptorToLxpack: () => assessmentDescriptorToLxpack,
24
+ buildLessonkitProject: () => buildLessonkitProject,
25
+ descriptorToInterchange: () => descriptorToInterchange,
26
+ extractAssessments: () => extractAssessments,
27
+ mapLessonkitIds: () => mapLessonkitIds,
28
+ packageLessonkitCourse: () => packageLessonkitCourse,
29
+ resolveSpaLessons: () => resolveSpaLessons,
30
+ themeToLxpackRuntime: () => themeToLxpackRuntime,
31
+ validateDescriptor: () => validateDescriptor,
32
+ validateLessonkitProject: () => validateLessonkitProject,
33
+ writeLxpackProject: () => writeLxpackProject
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+
37
+ // src/validateDescriptor.ts
38
+ var import_core = require("@lessonkit/core");
39
+
40
+ // src/spaPath.ts
41
+ var import_node_path = require("path");
42
+ function isSafeRelativeSpaPath(spaPath) {
43
+ if (!spaPath.length || spaPath.includes("\0")) return false;
44
+ if (spaPath.startsWith("/") || spaPath.startsWith("\\")) return false;
45
+ if (/^[a-zA-Z]:[/\\]/.test(spaPath)) return false;
46
+ const segments = spaPath.split(/[/\\]/);
47
+ if (segments.some((s) => s === "..")) return false;
48
+ return true;
49
+ }
50
+ function assertResolvedPathUnderRoot(root, target) {
51
+ const rootResolved = (0, import_node_path.resolve)(root);
52
+ const targetResolved = (0, import_node_path.resolve)(target);
53
+ const prefix = rootResolved.endsWith(import_node_path.sep) ? rootResolved : rootResolved + import_node_path.sep;
54
+ if (targetResolved !== rootResolved && !targetResolved.startsWith(prefix)) {
55
+ throw new Error(`unsafe path escapes project root: ${target}`);
56
+ }
57
+ }
58
+
59
+ // src/validateDescriptor.ts
60
+ function normalizeDescriptor(input) {
61
+ const course = (0, import_core.validateId)(input.courseId, "courseId");
62
+ if (!course.ok) throw new Error("normalizeDescriptor called with invalid courseId");
63
+ return {
64
+ ...input,
65
+ courseId: course.id,
66
+ title: input.title.trim(),
67
+ version: input.version?.trim() || void 0,
68
+ spaLessonId: input.spaLessonId?.trim() || void 0,
69
+ lessons: input.lessons.map((lesson) => {
70
+ const idResult = (0, import_core.validateId)(lesson.id, "lessonId");
71
+ if (!idResult.ok) throw new Error("normalizeDescriptor called with invalid lesson id");
72
+ return {
73
+ ...lesson,
74
+ id: idResult.id,
75
+ title: lesson.title.trim(),
76
+ spaPath: lesson.spaPath?.trim() || void 0
77
+ };
78
+ }),
79
+ assessments: input.assessments?.map((assessment) => {
80
+ const check = (0, import_core.validateId)(assessment.checkId, "checkId");
81
+ if (!check.ok) throw new Error("normalizeDescriptor called with invalid checkId");
82
+ return {
83
+ ...assessment,
84
+ checkId: check.id,
85
+ question: assessment.question.trim(),
86
+ choices: assessment.choices.map((c) => c.trim()).filter((c) => c.length > 0),
87
+ answer: assessment.answer.trim()
88
+ };
89
+ })
90
+ };
91
+ }
92
+ function validateDescriptor(input) {
93
+ const issues = [];
94
+ const course = (0, import_core.validateId)(input.courseId, "courseId");
95
+ if (!course.ok) issues.push(...course.issues.map((i) => ({ path: i.path, message: i.message })));
96
+ if (!input.title?.trim()) {
97
+ issues.push({ path: "title", message: "title is required" });
98
+ }
99
+ if (!input.lessons?.length) {
100
+ issues.push({ path: "lessons", message: "at least one lesson is required" });
101
+ }
102
+ if (input.layout === "single-spa" && (input.lessons?.length ?? 0) > 1) {
103
+ issues.push({
104
+ path: "lessons",
105
+ message: "single-spa layout packages one SPA lesson; remove extra lesson entries or use per-lesson-spa"
106
+ });
107
+ }
108
+ const lessonIds = /* @__PURE__ */ new Set();
109
+ const spaPaths = /* @__PURE__ */ new Set();
110
+ for (const [index, lesson] of (input.lessons ?? []).entries()) {
111
+ const path = `lessons[${index}]`;
112
+ const lessonResult = (0, import_core.validateId)(lesson.id, `${path}.id`);
113
+ if (!lessonResult.ok) {
114
+ issues.push(...lessonResult.issues.map((i) => ({ path: i.path, message: i.message })));
115
+ } else if (lessonIds.has(lessonResult.id)) {
116
+ issues.push({ path: `${path}.id`, message: "duplicate lesson id" });
117
+ } else {
118
+ lessonIds.add(lessonResult.id);
119
+ }
120
+ if (!lesson.title?.trim()) {
121
+ issues.push({ path: `${path}.title`, message: "lesson title is required" });
122
+ }
123
+ if (input.layout === "per-lesson-spa") {
124
+ const spaPath = lesson.spaPath?.trim();
125
+ if (!spaPath) {
126
+ issues.push({
127
+ path: `${path}.spaPath`,
128
+ message: "spaPath is required for per-lesson-spa layout"
129
+ });
130
+ } else if (!isSafeRelativeSpaPath(spaPath)) {
131
+ issues.push({
132
+ path: `${path}.spaPath`,
133
+ message: "spaPath must be a relative path without '..' segments or absolute prefixes"
134
+ });
135
+ } else if (spaPaths.has(spaPath)) {
136
+ issues.push({ path: `${path}.spaPath`, message: "duplicate spaPath" });
137
+ } else {
138
+ spaPaths.add(spaPath);
139
+ }
140
+ }
141
+ }
142
+ if (input.layout === "single-spa" && input.spaLessonId?.trim()) {
143
+ const spaId = input.spaLessonId.trim();
144
+ const spaResult = (0, import_core.validateId)(spaId, "spaLessonId");
145
+ if (!spaResult.ok) {
146
+ issues.push(...spaResult.issues.map((i) => ({ path: i.path, message: i.message })));
147
+ } else if (!lessonIds.has(spaResult.id)) {
148
+ issues.push({
149
+ path: "spaLessonId",
150
+ message: "spaLessonId must match a lesson id in lessons"
151
+ });
152
+ }
153
+ }
154
+ const checkIds = /* @__PURE__ */ new Set();
155
+ for (const [index, assessment] of (input.assessments ?? []).entries()) {
156
+ const path = `assessments[${index}]`;
157
+ const check = (0, import_core.validateId)(assessment.checkId, `${path}.checkId`);
158
+ if (!check.ok) {
159
+ issues.push(...check.issues.map((i) => ({ path: i.path, message: i.message })));
160
+ } else if (checkIds.has(check.id)) {
161
+ issues.push({ path: `${path}.checkId`, message: "duplicate checkId" });
162
+ } else {
163
+ checkIds.add(check.id);
164
+ }
165
+ if (!assessment.question?.trim()) {
166
+ issues.push({ path: `${path}.question`, message: "question is required" });
167
+ }
168
+ const trimmedChoices = (assessment.choices ?? []).map((c) => c.trim()).filter((c) => c.length > 0);
169
+ if (!trimmedChoices.length) {
170
+ issues.push({
171
+ path: `${path}.choices`,
172
+ message: "at least one non-empty choice is required"
173
+ });
174
+ }
175
+ if (!assessment.answer?.trim()) {
176
+ issues.push({ path: `${path}.answer`, message: "answer is required" });
177
+ } else if (trimmedChoices.length && !trimmedChoices.includes(assessment.answer.trim())) {
178
+ issues.push({ path: `${path}.answer`, message: "answer must match a choice" });
179
+ }
180
+ const passingScore = assessment.passingScore;
181
+ if (passingScore !== void 0) {
182
+ if (!(passingScore > 0)) {
183
+ issues.push({
184
+ path: `${path}.passingScore`,
185
+ message: "passingScore must be greater than 0"
186
+ });
187
+ } else if (trimmedChoices.length && passingScore > trimmedChoices.length) {
188
+ issues.push({
189
+ path: `${path}.passingScore`,
190
+ message: "passingScore must not exceed the number of choices"
191
+ });
192
+ }
193
+ }
194
+ }
195
+ if (issues.length) return { ok: false, issues };
196
+ return { ok: true, descriptor: normalizeDescriptor(input) };
197
+ }
198
+
199
+ // src/mapIds.ts
200
+ var import_core2 = require("@lessonkit/core");
201
+ function mapLessonkitIds(descriptor) {
202
+ const courseId = (0, import_core2.assertValidId)(descriptor.courseId, "courseId");
203
+ const lessonIds = descriptor.lessons.map((l) => (0, import_core2.assertValidId)(l.id, "lessonId"));
204
+ const checkIds = (descriptor.assessments ?? []).map(
205
+ (a) => (0, import_core2.assertValidId)(a.checkId, "checkId")
206
+ );
207
+ return { courseId, lessonIds, checkIds };
208
+ }
209
+
210
+ // src/theme.ts
211
+ var import_themes = require("@lessonkit/themes");
212
+ function themeToLxpackRuntime(input) {
213
+ const theme = input.theme ?? (0, import_themes.getPresetTheme)(input.preset ?? "default");
214
+ const raw = (0, import_themes.themeToCssVariables)(theme);
215
+ const cssVariables = {};
216
+ for (const [key, value] of Object.entries(raw)) {
217
+ cssVariables[key] = String(value);
218
+ }
219
+ return {
220
+ theme: theme.name,
221
+ cssVariables
222
+ };
223
+ }
224
+
225
+ // src/interchange.ts
226
+ function resolveSpaLessons(descriptor) {
227
+ const mapped = mapLessonkitIds(descriptor);
228
+ if (descriptor.layout === "single-spa") {
229
+ const spaLessonId = descriptor.spaLessonId ?? mapped.lessonIds[0] ?? "main";
230
+ const firstLesson = descriptor.lessons.find((l) => l.id === spaLessonId);
231
+ return [
232
+ {
233
+ id: spaLessonId,
234
+ title: firstLesson?.title ?? descriptor.title,
235
+ path: "dist"
236
+ }
237
+ ];
238
+ }
239
+ return descriptor.lessons.map((lesson) => ({
240
+ id: lesson.id,
241
+ title: lesson.title,
242
+ path: lesson.spaPath
243
+ }));
244
+ }
245
+ function descriptorToInterchange(descriptor) {
246
+ const mapped = mapLessonkitIds(descriptor);
247
+ const spaLessons = resolveSpaLessons(descriptor);
248
+ return {
249
+ format: "lessonkit",
250
+ version: "1",
251
+ course: {
252
+ id: mapped.courseId,
253
+ title: descriptor.title
254
+ },
255
+ lessons: spaLessons.map((l) => ({
256
+ id: l.id,
257
+ title: l.title,
258
+ type: "spa",
259
+ path: l.path
260
+ })),
261
+ tracking: descriptor.tracking
262
+ };
263
+ }
264
+
265
+ // src/assessments.ts
266
+ function slugChoiceId(text, index) {
267
+ const base = text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 32);
268
+ const stem = base.length ? base : "choice";
269
+ return `${stem}-${index + 1}`;
270
+ }
271
+ function assessmentDescriptorToLxpack(assessment) {
272
+ const choices = assessment.choices.map((text, index) => {
273
+ const id = slugChoiceId(text, index);
274
+ return {
275
+ id,
276
+ text,
277
+ correct: text === assessment.answer
278
+ };
279
+ });
280
+ return {
281
+ id: assessment.checkId,
282
+ passingScore: assessment.passingScore ?? 1,
283
+ questions: [
284
+ {
285
+ id: "q1",
286
+ prompt: assessment.question,
287
+ choices
288
+ }
289
+ ]
290
+ };
291
+ }
292
+ function extractAssessments(descriptor) {
293
+ return (descriptor.assessments ?? []).map(assessmentDescriptorToLxpack);
294
+ }
295
+
296
+ // src/writeProject.ts
297
+ var import_promises = require("fs/promises");
298
+ var import_node_path2 = require("path");
299
+
300
+ // src/assessmentYaml.ts
301
+ function yamlQuote(value) {
302
+ return JSON.stringify(value);
303
+ }
304
+ function emitAssessmentYaml(assessment) {
305
+ const lx = assessmentDescriptorToLxpack(assessment);
306
+ const lines = [];
307
+ lines.push(`id: ${lx.id}`);
308
+ lines.push(`passingScore: ${lx.passingScore}`);
309
+ lines.push("questions:");
310
+ for (const question of lx.questions) {
311
+ lines.push(` - id: ${question.id}`);
312
+ lines.push(` prompt: ${yamlQuote(question.prompt)}`);
313
+ lines.push(" choices:");
314
+ for (const choice of question.choices) {
315
+ lines.push(` - id: ${choice.id}`);
316
+ lines.push(` text: ${yamlQuote(choice.text)}`);
317
+ if (choice.correct) lines.push(" correct: true");
318
+ }
319
+ }
320
+ return `${lines.join("\n")}
321
+ `;
322
+ }
323
+
324
+ // src/yaml.ts
325
+ function yamlQuote2(value) {
326
+ if (/[:#\n\r]/.test(value) || value.startsWith(" ") || value.endsWith(" ")) {
327
+ return JSON.stringify(value);
328
+ }
329
+ return value;
330
+ }
331
+ function emitCourseYaml(opts) {
332
+ const lines = [];
333
+ lines.push(`title: ${yamlQuote2(opts.title)}`);
334
+ lines.push(`version: ${yamlQuote2(opts.version)}`);
335
+ if (opts.description) lines.push(`description: ${yamlQuote2(opts.description)}`);
336
+ if (opts.runtime) {
337
+ lines.push("runtime:");
338
+ lines.push(` theme: ${yamlQuote2(opts.runtime.theme)}`);
339
+ if (opts.runtime.cssVariables && Object.keys(opts.runtime.cssVariables).length) {
340
+ lines.push(" cssVariables:");
341
+ for (const [key, value] of Object.entries(opts.runtime.cssVariables).sort(
342
+ ([a], [b]) => a.localeCompare(b)
343
+ )) {
344
+ lines.push(` ${key}: ${JSON.stringify(String(value))}`);
345
+ }
346
+ }
347
+ }
348
+ if (opts.tracking?.completion?.threshold !== void 0) {
349
+ lines.push("tracking:");
350
+ lines.push(" completion:");
351
+ lines.push(` threshold: ${opts.tracking.completion.threshold}`);
352
+ }
353
+ lines.push("lessons:");
354
+ for (const lesson of opts.lessons) {
355
+ lines.push(` - id: ${lesson.id}`);
356
+ lines.push(` title: ${yamlQuote2(lesson.title)}`);
357
+ lines.push(` type: spa`);
358
+ lines.push(` path: ${lesson.path}`);
359
+ }
360
+ if (opts.assessments.length) {
361
+ lines.push("assessments:");
362
+ for (const assessment of opts.assessments) {
363
+ lines.push(` - id: ${assessment.id}`);
364
+ lines.push(` file: ${assessment.file}`);
365
+ }
366
+ }
367
+ return `${lines.join("\n")}
368
+ `;
369
+ }
370
+
371
+ // src/writeProject.ts
372
+ async function copyDir(src, dest) {
373
+ await (0, import_promises.mkdir)((0, import_node_path2.dirname)(dest), { recursive: true });
374
+ await (0, import_promises.cp)(src, dest, { recursive: true });
375
+ }
376
+ async function writeLxpackProject(options) {
377
+ const validation = validateDescriptor(options.descriptor);
378
+ if (!validation.ok) {
379
+ throw new Error(
380
+ validation.issues.map((i) => `${i.path}: ${i.message}`).join("; ")
381
+ );
382
+ }
383
+ const descriptor = validation.descriptor;
384
+ const outDir = (0, import_node_path2.resolve)(options.outDir);
385
+ await (0, import_promises.mkdir)(outDir, { recursive: true });
386
+ const spaLessons = resolveSpaLessons(descriptor);
387
+ const runtime = descriptor.theme ? themeToLxpackRuntime(descriptor.theme) : void 0;
388
+ const assessments = (descriptor.assessments ?? []).map((a) => ({
389
+ id: a.checkId,
390
+ file: `assessments/${a.checkId}.yaml`
391
+ }));
392
+ if (descriptor.layout === "single-spa") {
393
+ const srcDist = (0, import_node_path2.resolve)(options.spaDistDir ?? descriptor.spaDistDir ?? "dist");
394
+ try {
395
+ await (0, import_promises.access)(srcDist);
396
+ } catch {
397
+ throw new Error(`spaDistDir not found: ${srcDist}`);
398
+ }
399
+ const destDist = (0, import_node_path2.join)(outDir, "dist");
400
+ await (0, import_promises.rm)(destDist, { recursive: true, force: true });
401
+ await copyDir(srcDist, destDist);
402
+ } else {
403
+ const lessonDirs = options.lessonSpaDirs ?? {};
404
+ for (const lesson of descriptor.lessons) {
405
+ const src = lessonDirs[lesson.id];
406
+ if (!src) {
407
+ throw new Error(`lessonSpaDirs missing build output for lesson "${lesson.id}"`);
408
+ }
409
+ const dest = (0, import_node_path2.join)(outDir, lesson.spaPath);
410
+ assertResolvedPathUnderRoot(outDir, dest);
411
+ await (0, import_promises.rm)(dest, { recursive: true, force: true });
412
+ await copyDir((0, import_node_path2.resolve)(src), dest);
413
+ }
414
+ }
415
+ if (assessments.length) {
416
+ const assessmentsDir = (0, import_node_path2.join)(outDir, "assessments");
417
+ await (0, import_promises.mkdir)(assessmentsDir, { recursive: true });
418
+ for (const assessment of descriptor.assessments ?? []) {
419
+ await (0, import_promises.writeFile)(
420
+ (0, import_node_path2.join)(outDir, `assessments/${assessment.checkId}.yaml`),
421
+ emitAssessmentYaml(assessment),
422
+ "utf-8"
423
+ );
424
+ }
425
+ }
426
+ const courseYamlPath = (0, import_node_path2.join)(outDir, "course.yaml");
427
+ await (0, import_promises.writeFile)(
428
+ courseYamlPath,
429
+ emitCourseYaml({
430
+ title: descriptor.title,
431
+ version: descriptor.version ?? "1.0.0",
432
+ runtime,
433
+ tracking: descriptor.tracking,
434
+ lessons: spaLessons.map((l) => ({
435
+ id: l.id,
436
+ title: l.title,
437
+ type: "spa",
438
+ path: l.path
439
+ })),
440
+ assessments
441
+ }),
442
+ "utf-8"
443
+ );
444
+ const lessonkitJsonPath = (0, import_node_path2.join)(outDir, "lessonkit.json");
445
+ await (0, import_promises.writeFile)(
446
+ lessonkitJsonPath,
447
+ `${JSON.stringify(descriptorToInterchange(descriptor), null, 2)}
448
+ `,
449
+ "utf-8"
450
+ );
451
+ return { outDir, courseYamlPath, lessonkitJsonPath };
452
+ }
453
+
454
+ // src/packageCourse.ts
455
+ var import_promises2 = require("fs/promises");
456
+ var import_node_path3 = require("path");
457
+ var import_node_os = require("os");
458
+ var import_api = require("@lxpack/api");
459
+ async function validateLessonkitProject(options) {
460
+ return (0, import_api.validateCourse)({
461
+ courseDir: (0, import_node_path3.resolve)(options.courseDir),
462
+ target: options.target
463
+ });
464
+ }
465
+ async function buildLessonkitProject(options) {
466
+ return (0, import_api.buildCourse)({
467
+ courseDir: (0, import_node_path3.resolve)(options.courseDir),
468
+ target: options.target,
469
+ output: options.output,
470
+ dir: options.dir,
471
+ outputBaseDir: options.outputBaseDir,
472
+ assessments: options.assessments
473
+ });
474
+ }
475
+ async function packageLessonkitCourse(options) {
476
+ const { target, output, dir, outputBaseDir, ...writeOpts } = options;
477
+ const outDir = (0, import_node_path3.resolve)(writeOpts.outDir);
478
+ const descriptorValidation = validateDescriptor(writeOpts.descriptor);
479
+ if (!descriptorValidation.ok) {
480
+ return {
481
+ ok: false,
482
+ courseDir: outDir,
483
+ target,
484
+ issues: descriptorValidation.issues.map((i) => ({
485
+ path: i.path,
486
+ message: i.message
487
+ }))
488
+ };
489
+ }
490
+ const descriptor = descriptorValidation.descriptor;
491
+ const stagingDir = await (0, import_promises2.mkdtemp)((0, import_node_path3.join)((0, import_node_os.tmpdir)(), "lessonkit-lxpack-"));
492
+ let promoted = false;
493
+ try {
494
+ const written = await writeLxpackProject({ ...writeOpts, descriptor, outDir: stagingDir });
495
+ const courseDir = written.outDir;
496
+ const assessments = extractAssessments(descriptor);
497
+ const validation = await validateLessonkitProject({ courseDir, target });
498
+ if (!validation.ok) {
499
+ return {
500
+ ok: false,
501
+ courseDir: outDir,
502
+ target,
503
+ validation,
504
+ issues: validation.issues.map((i) => ({
505
+ path: i.path,
506
+ message: i.message,
507
+ severity: i.severity
508
+ }))
509
+ };
510
+ }
511
+ const outputBase = outputBaseDir ?? ".lxpack/out";
512
+ await (0, import_promises2.mkdir)((0, import_node_path3.join)(courseDir, outputBase), { recursive: true });
513
+ const defaultOutput = output ?? (dir ? (0, import_node_path3.join)(outputBase, target) : (0, import_node_path3.join)(outputBase, `course-${target}.zip`));
514
+ const build = await buildLessonkitProject({
515
+ courseDir,
516
+ target,
517
+ output: defaultOutput.startsWith("/") ? defaultOutput : (0, import_node_path3.join)(courseDir, defaultOutput),
518
+ dir,
519
+ assessments: assessments.length ? assessments : void 0
520
+ });
521
+ if (!build.ok) {
522
+ return {
523
+ ok: false,
524
+ courseDir: outDir,
525
+ target,
526
+ validation,
527
+ build,
528
+ issues: build.issues.map((i) => ({
529
+ path: i.path,
530
+ message: i.message,
531
+ severity: i.severity
532
+ }))
533
+ };
534
+ }
535
+ await (0, import_promises2.rm)(outDir, { recursive: true, force: true });
536
+ await (0, import_promises2.mkdir)((0, import_node_path3.dirname)(outDir), { recursive: true });
537
+ await (0, import_promises2.rename)(stagingDir, outDir);
538
+ promoted = true;
539
+ const remapArtifactPath = (artifactPath) => {
540
+ if (!artifactPath) return void 0;
541
+ const resolved = (0, import_node_path3.resolve)(artifactPath);
542
+ const stagingResolved = (0, import_node_path3.resolve)(stagingDir);
543
+ if (resolved === stagingResolved || resolved.startsWith(stagingResolved + "/")) {
544
+ return (0, import_node_path3.join)(outDir, resolved.slice(stagingResolved.length + 1));
545
+ }
546
+ return artifactPath;
547
+ };
548
+ return {
549
+ ok: true,
550
+ courseDir: outDir,
551
+ target,
552
+ outputPath: remapArtifactPath("outputPath" in build ? build.outputPath : void 0),
553
+ outputDir: remapArtifactPath("outputDir" in build ? build.outputDir : void 0),
554
+ fileCount: build.fileCount,
555
+ validation,
556
+ build
557
+ };
558
+ } finally {
559
+ if (!promoted) {
560
+ await (0, import_promises2.rm)(stagingDir, { recursive: true, force: true }).catch(() => void 0);
561
+ }
562
+ }
563
+ }
564
+ // Annotate the CommonJS export names for ESM import in node:
565
+ 0 && (module.exports = {
566
+ assessmentDescriptorToLxpack,
567
+ buildLessonkitProject,
568
+ descriptorToInterchange,
569
+ extractAssessments,
570
+ mapLessonkitIds,
571
+ packageLessonkitCourse,
572
+ resolveSpaLessons,
573
+ themeToLxpackRuntime,
574
+ validateDescriptor,
575
+ validateLessonkitProject,
576
+ writeLxpackProject
577
+ });