@learnpack/learnpack 5.0.213 → 5.0.217
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 +13 -13
- package/lib/commands/serve.d.ts +1 -1
- package/lib/commands/serve.js +79 -52
- package/lib/creatorDist/assets/{index-DxwqeFD3.js → index-CZrxF_55.js} +2807 -2763
- package/lib/creatorDist/assets/{index-Bnq3eZ3T.css → index-DmpsXknz.css} +6 -0
- package/lib/creatorDist/index.html +2 -2
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/commands/serve.ts +99 -62
- package/src/creator/src/components/FileUploader.tsx +35 -3
- package/src/creator/src/locales/en.json +8 -3
- package/src/creator/src/locales/es.json +7 -2
- package/src/creator/src/utils/constants.ts +2 -1
- package/src/creator/src/utils/lib.ts +9 -1
- package/src/creatorDist/assets/{index-DxwqeFD3.js → index-CZrxF_55.js} +2807 -2763
- package/src/creatorDist/assets/{index-Bnq3eZ3T.css → index-DmpsXknz.css} +6 -0
- package/src/creatorDist/index.html +2 -2
- package/src/ui/_app/app.css +1 -1
- package/src/ui/_app/app.js +326 -326
- package/src/ui/app.tar.gz +0 -0
package/README.md
CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @learnpack/learnpack
|
|
21
21
|
$ learnpack COMMAND
|
22
22
|
running command...
|
23
23
|
$ learnpack (-v|--version|version)
|
24
|
-
@learnpack/learnpack/5.0.
|
24
|
+
@learnpack/learnpack/5.0.217 win32-x64 node-v22.15.0
|
25
25
|
$ learnpack --help [COMMAND]
|
26
26
|
USAGE
|
27
27
|
$ learnpack COMMAND
|
@@ -80,7 +80,7 @@ DESCRIPTION
|
|
80
80
|
12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
|
81
81
|
```
|
82
82
|
|
83
|
-
_See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
83
|
+
_See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.217/src\commands\audit.ts)_
|
84
84
|
|
85
85
|
## `learnpack breakToken`
|
86
86
|
|
@@ -95,7 +95,7 @@ OPTIONS
|
|
95
95
|
-y, --yes Skip all prompts and initialize an empty project
|
96
96
|
```
|
97
97
|
|
98
|
-
_See code: [src\commands\breakToken.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
98
|
+
_See code: [src\commands\breakToken.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.217/src\commands\breakToken.ts)_
|
99
99
|
|
100
100
|
## `learnpack clean`
|
101
101
|
|
@@ -110,7 +110,7 @@ DESCRIPTION
|
|
110
110
|
Extra documentation goes here
|
111
111
|
```
|
112
112
|
|
113
|
-
_See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
113
|
+
_See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.217/src\commands\clean.ts)_
|
114
114
|
|
115
115
|
## `learnpack download [PACKAGE]`
|
116
116
|
|
@@ -128,7 +128,7 @@ DESCRIPTION
|
|
128
128
|
Extra documentation goes here
|
129
129
|
```
|
130
130
|
|
131
|
-
_See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
131
|
+
_See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.217/src\commands\download.ts)_
|
132
132
|
|
133
133
|
## `learnpack help [COMMAND]`
|
134
134
|
|
@@ -160,7 +160,7 @@ OPTIONS
|
|
160
160
|
-y, --yes Skip all prompts and initialize an empty project
|
161
161
|
```
|
162
162
|
|
163
|
-
_See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
163
|
+
_See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.217/src\commands\init.ts)_
|
164
164
|
|
165
165
|
## `learnpack login [PACKAGE]`
|
166
166
|
|
@@ -178,7 +178,7 @@ DESCRIPTION
|
|
178
178
|
Extra documentation goes here
|
179
179
|
```
|
180
180
|
|
181
|
-
_See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
181
|
+
_See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.217/src\commands\login.ts)_
|
182
182
|
|
183
183
|
## `learnpack logout [PACKAGE]`
|
184
184
|
|
@@ -196,7 +196,7 @@ DESCRIPTION
|
|
196
196
|
Extra documentation goes here
|
197
197
|
```
|
198
198
|
|
199
|
-
_See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
199
|
+
_See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.217/src\commands\logout.ts)_
|
200
200
|
|
201
201
|
## `learnpack plugins`
|
202
202
|
|
@@ -328,7 +328,7 @@ OPTIONS
|
|
328
328
|
-s, --strict strict mode
|
329
329
|
```
|
330
330
|
|
331
|
-
_See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
331
|
+
_See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.217/src\commands\publish.ts)_
|
332
332
|
|
333
333
|
## `learnpack serve`
|
334
334
|
|
@@ -345,7 +345,7 @@ OPTIONS
|
|
345
345
|
-y, --yes Skip all prompts and initialize an empty project
|
346
346
|
```
|
347
347
|
|
348
|
-
_See code: [src\commands\serve.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
348
|
+
_See code: [src\commands\serve.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.217/src\commands\serve.ts)_
|
349
349
|
|
350
350
|
## `learnpack start`
|
351
351
|
|
@@ -367,7 +367,7 @@ OPTIONS
|
|
367
367
|
-y, --yes Skip all prompts and initialize an empty project
|
368
368
|
```
|
369
369
|
|
370
|
-
_See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
370
|
+
_See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.217/src\commands\start.ts)_
|
371
371
|
|
372
372
|
## `learnpack test [EXERCISESLUG]`
|
373
373
|
|
@@ -384,7 +384,7 @@ OPTIONS
|
|
384
384
|
-y, --yes Skip all prompts and initialize an empty project
|
385
385
|
```
|
386
386
|
|
387
|
-
_See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
387
|
+
_See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.217/src\commands\test.ts)_
|
388
388
|
|
389
389
|
## `learnpack translate`
|
390
390
|
|
@@ -398,7 +398,7 @@ OPTIONS
|
|
398
398
|
-y, --yes Skip all prompts and initialize an empty project
|
399
399
|
```
|
400
400
|
|
401
|
-
_See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
401
|
+
_See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.217/src\commands\translate.ts)_
|
402
402
|
<!-- commandsstop -->
|
403
403
|
|
404
404
|
> > > > > > > 0cb3e56d84c197f9d008836bb573eade212b7e57
|
package/lib/commands/serve.d.ts
CHANGED
package/lib/commands/serve.js
CHANGED
@@ -48,18 +48,17 @@ const createLearnJson = (courseInfo) => {
|
|
48
48
|
const language = courseInfo.language || "en";
|
49
49
|
const learnJson = {
|
50
50
|
slug: (0, creatorUtilities_2.slugify)(courseInfo.title),
|
51
|
-
title: language === "en" ?
|
51
|
+
title: language === "en" || language === "us" ?
|
52
52
|
{
|
53
53
|
us: courseInfo.title,
|
54
54
|
} :
|
55
55
|
{
|
56
56
|
[language]: courseInfo.title,
|
57
|
-
// us: courseInfo.title,
|
58
57
|
},
|
59
58
|
technologies: courseInfo.technologies || [],
|
60
59
|
difficulty: "beginner",
|
61
60
|
description: {
|
62
|
-
us: courseInfo.description,
|
61
|
+
[language === "en" || language === "us" ? "us" : language]: courseInfo.description,
|
63
62
|
},
|
64
63
|
grading: "isolated",
|
65
64
|
telemetry: {
|
@@ -277,7 +276,7 @@ class ServeCommand extends SessionCommand_1.default {
|
|
277
276
|
const distPath = path.resolve(__dirname, "../creatorDist");
|
278
277
|
// Servir archivos estáticos
|
279
278
|
// app.use(express.static(distPath))
|
280
|
-
app.use(express.json());
|
279
|
+
app.use(express.json({ limit: "20mb" }));
|
281
280
|
app.use(cors());
|
282
281
|
const appPath = path.resolve(__dirname, "../ui/_app");
|
283
282
|
const tarPath = path.resolve(__dirname, "../ui/app.tar.gz");
|
@@ -316,35 +315,6 @@ class ServeCommand extends SessionCommand_1.default {
|
|
316
315
|
});
|
317
316
|
stream.end(buffer);
|
318
317
|
});
|
319
|
-
app.post("/upload-image", express.json({ limit: "10mb" }), async (req, res) => {
|
320
|
-
const { image_url, destination } = req.body;
|
321
|
-
if (!image_url || !destination) {
|
322
|
-
return res
|
323
|
-
.status(400)
|
324
|
-
.json({ error: "image_url and destination are required" });
|
325
|
-
}
|
326
|
-
try {
|
327
|
-
const response = await fetch(image_url);
|
328
|
-
if (!response.ok) {
|
329
|
-
return res.status(400).json({
|
330
|
-
error: `Failed to download image: ${response.statusText}`,
|
331
|
-
});
|
332
|
-
}
|
333
|
-
const contentType = response.headers.get("content-type") || "application/octet-stream";
|
334
|
-
const buffer = await response.arrayBuffer();
|
335
|
-
const file = bucket.file(destination);
|
336
|
-
await file.save(Buffer.from(buffer), {
|
337
|
-
resumable: false,
|
338
|
-
contentType,
|
339
|
-
});
|
340
|
-
console.log(`✅ Image uploaded to ${file.name}`);
|
341
|
-
res.json({ message: "Image uploaded successfully", path: file.name });
|
342
|
-
}
|
343
|
-
catch (error) {
|
344
|
-
console.error("❌ upload-image error:", error);
|
345
|
-
res.status(500).json({ error: error.message });
|
346
|
-
}
|
347
|
-
});
|
348
318
|
const upload = (0, misc_1.createUploadMiddleware)();
|
349
319
|
app.post("/upload-image-file", upload.single("file"), async (req, res) => {
|
350
320
|
console.log("UPLOADING IMAGE FILE");
|
@@ -420,6 +390,35 @@ class ServeCommand extends SessionCommand_1.default {
|
|
420
390
|
(0, creatorSocket_1.emitToNotification)(id, body);
|
421
391
|
res.json({ id, status: "SUCCESS" });
|
422
392
|
});
|
393
|
+
app.post("/webhooks/:courseSlug/images/:imageId", async (req, res) => {
|
394
|
+
const { courseSlug, imageId } = req.params;
|
395
|
+
const body = req.body;
|
396
|
+
console.log("RECEIVING IMAGE WEBHOOK", body);
|
397
|
+
const imageUrl = body.image_url;
|
398
|
+
const imagePath = `courses/${courseSlug}/.learn/assets/${imageId}`;
|
399
|
+
const imageFile = bucket.file(imagePath);
|
400
|
+
const [exists] = await imageFile.exists();
|
401
|
+
if (!exists) {
|
402
|
+
// Descargar la imagen
|
403
|
+
const response = await fetch(imageUrl);
|
404
|
+
if (!response.ok) {
|
405
|
+
return res.status(400).json({
|
406
|
+
status: "ERROR",
|
407
|
+
message: "No se pudo descargar la imagen",
|
408
|
+
});
|
409
|
+
}
|
410
|
+
const buffer = await response.arrayBuffer();
|
411
|
+
// Guardar la imagen en el bucket
|
412
|
+
await imageFile.save(new Uint8Array(buffer), {
|
413
|
+
contentType: "image/png",
|
414
|
+
});
|
415
|
+
}
|
416
|
+
(0, creatorSocket_1.emitToNotification)(imageId, {
|
417
|
+
status: "SUCCESS",
|
418
|
+
message: "Image generated successfully",
|
419
|
+
});
|
420
|
+
res.json({ status: "SUCCESS" });
|
421
|
+
});
|
423
422
|
app.post("/webhooks/:courseSlug/exercise-processor/:lessonID/:rigoToken", async (req, res) => {
|
424
423
|
// console.log("Receiving a webhook to exercise processor")
|
425
424
|
const { courseSlug, lessonID, rigoToken } = req.params;
|
@@ -542,25 +541,38 @@ class ServeCommand extends SessionCommand_1.default {
|
|
542
541
|
const { slug } = req.params;
|
543
542
|
const courseSlug = req.query.slug;
|
544
543
|
const lang = req.query.lang || "us";
|
544
|
+
if (!courseSlug) {
|
545
|
+
return res.status(400).json({ error: "Missing courseSlug" });
|
546
|
+
}
|
545
547
|
const basePath = `courses/${courseSlug}/exercises/${slug}/`;
|
546
|
-
const
|
547
|
-
const file = bucket.file(basePath + filename);
|
548
|
-
let contentBuffer;
|
548
|
+
const prefix = basePath + "README";
|
549
549
|
try {
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
console.warn(`No README for lang '${lang}', falling back to 'us'`);
|
555
|
-
const fallbackFile = bucket.file(basePath + "README.md");
|
556
|
-
contentBuffer = await fallbackFile.download();
|
550
|
+
const [files] = await bucket.getFiles({ prefix });
|
551
|
+
const readmeFiles = files.map(f => f.name);
|
552
|
+
if (readmeFiles.length === 0) {
|
553
|
+
return res.status(404).json({ error: "No README files found" });
|
557
554
|
}
|
558
|
-
|
559
|
-
|
555
|
+
const requestedFilename = lang === "us" || lang === "en" ?
|
556
|
+
`${basePath}README.md` :
|
557
|
+
`${basePath}README.${lang}.md`;
|
558
|
+
let selectedFile = readmeFiles.find(f => f === requestedFilename);
|
559
|
+
if (!selectedFile) {
|
560
|
+
selectedFile = readmeFiles[0];
|
561
|
+
console.warn(`Requested README not found for lang '${lang}', using '${selectedFile}'`);
|
560
562
|
}
|
563
|
+
let foundLang = "us";
|
564
|
+
const match = selectedFile.match(/readme(?:\.([a-z]{2}))?\.md$/i);
|
565
|
+
if (match && match[1]) {
|
566
|
+
foundLang = match[1].toLowerCase();
|
567
|
+
}
|
568
|
+
const [contentBuffer] = await bucket.file(selectedFile).download();
|
569
|
+
const { attributes, body } = frontMatter(contentBuffer.toString());
|
570
|
+
res.send({ attributes, body, lang: foundLang });
|
571
|
+
}
|
572
|
+
catch (error) {
|
573
|
+
console.error(error);
|
574
|
+
res.status(500).json({ error: "Internal server error" });
|
561
575
|
}
|
562
|
-
const { attributes, body } = frontMatter(contentBuffer[0].toString());
|
563
|
-
res.send({ attributes, body });
|
564
576
|
});
|
565
577
|
app.get("/.learn/assets/:file", async (req, res) => {
|
566
578
|
console.log("GET /.learn/assets/:file", req.params.file);
|
@@ -585,12 +597,12 @@ class ServeCommand extends SessionCommand_1.default {
|
|
585
597
|
const query = req.query;
|
586
598
|
console.log(`PUT /exercise/${slug}/file/${fileName}`);
|
587
599
|
const courseSlug = query.slug;
|
588
|
-
console.log("COURSE SLUG", courseSlug)
|
600
|
+
// console.log("COURSE SLUG", courseSlug)
|
589
601
|
// Update the file in the bucket
|
590
602
|
const file = await bucket.file(`courses/${courseSlug}/exercises/${slug}/${fileName}`);
|
591
603
|
await file.save(req.body);
|
592
604
|
const created = await file.exists();
|
593
|
-
console.log("File updated", created)
|
605
|
+
// console.log("File updated", created)
|
594
606
|
res.send({
|
595
607
|
message: "File updated",
|
596
608
|
created,
|
@@ -916,12 +928,27 @@ class ServeCommand extends SessionCommand_1.default {
|
|
916
928
|
}
|
917
929
|
};
|
918
930
|
copyDir(uiSrc, buildRoot);
|
931
|
+
const availableLangs = Object.keys(config.title);
|
932
|
+
let selectedLang = "us";
|
933
|
+
let title = "";
|
934
|
+
if (availableLangs.includes("us")) {
|
935
|
+
title = config.title.us;
|
936
|
+
}
|
937
|
+
else {
|
938
|
+
// Select the first available lang
|
939
|
+
title = config.title[availableLangs[0]];
|
940
|
+
selectedLang = availableLangs[0];
|
941
|
+
}
|
942
|
+
console.log(config.description, "CONFIG DESCRIPTION");
|
943
|
+
// console.log(availableLangs, "AVAILABLE LANGs")
|
944
|
+
// console.log(selectedLang, "SELECTED LANG")
|
945
|
+
// console.log(title, "TITLE")
|
919
946
|
// 5) Inyectar placeholders en index.html
|
920
947
|
const idxTpl = fs.readFileSync(path.join(uiSrc, "index.html"), "utf-8");
|
921
948
|
const idxHtml = idxTpl
|
922
|
-
.replace(/{{title}}/g,
|
923
|
-
.replace(/<title>.*<\/title>/, `<title>${
|
924
|
-
.replace(/{{description}}/g, config.description
|
949
|
+
.replace(/{{title}}/g, title)
|
950
|
+
.replace(/<title>.*<\/title>/, `<title>${title}</title>`)
|
951
|
+
.replace(/{{description}}/g, config.description[selectedLang])
|
925
952
|
.replace(/{{preview}}/g, fixPreviewUrl(slug, "") ||
|
926
953
|
"https://raw.githubusercontent.com/learnpack/ide/master/public/learnpack.svg")
|
927
954
|
.replace(/{{slug}}/g, slug)
|