@learnpack/learnpack 5.0.275 → 5.0.277

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.
Files changed (95) hide show
  1. package/README.md +409 -409
  2. package/lib/commands/audit.js +15 -15
  3. package/lib/commands/breakToken.js +19 -19
  4. package/lib/commands/clean.js +3 -3
  5. package/lib/commands/init.js +41 -41
  6. package/lib/commands/logout.js +3 -3
  7. package/lib/commands/publish.js +5 -10
  8. package/lib/commands/serve.js +55 -2
  9. package/lib/creatorDist/assets/index-BfLyIQVh.js +10343 -10224
  10. package/lib/managers/config/index.js +77 -77
  11. package/lib/utils/api.d.ts +1 -1
  12. package/lib/utils/api.js +12 -9
  13. package/lib/utils/creatorUtilities.js +14 -14
  14. package/lib/utils/export/epub.d.ts +2 -0
  15. package/lib/utils/export/epub.js +298 -0
  16. package/lib/utils/export/index.d.ts +3 -0
  17. package/lib/utils/export/index.js +7 -0
  18. package/lib/utils/export/scorm.d.ts +2 -0
  19. package/lib/utils/export/scorm.js +84 -0
  20. package/lib/utils/export/shared.d.ts +4 -0
  21. package/lib/utils/export/shared.js +61 -0
  22. package/lib/utils/export/types.d.ts +15 -0
  23. package/lib/utils/export/types.js +2 -0
  24. package/package.json +2 -1
  25. package/src/commands/audit.ts +487 -487
  26. package/src/commands/breakToken.ts +67 -67
  27. package/src/commands/clean.ts +30 -30
  28. package/src/commands/init.ts +650 -650
  29. package/src/commands/logout.ts +38 -38
  30. package/src/commands/publish.ts +20 -25
  31. package/src/commands/serve.ts +69 -4
  32. package/src/commands/start.ts +333 -333
  33. package/src/commands/translate.ts +123 -123
  34. package/src/creator/README.md +54 -54
  35. package/src/creator/eslint.config.js +7 -7
  36. package/src/creator/src/components/syllabus/ContentIndex.tsx +312 -312
  37. package/src/creator/src/i18n.ts +28 -28
  38. package/src/creator/src/index.css +217 -217
  39. package/src/creator/src/locales/en.json +126 -126
  40. package/src/creator/src/locales/es.json +126 -126
  41. package/src/creator/src/utils/configTypes.ts +122 -122
  42. package/src/creator/src/utils/constants.ts +13 -13
  43. package/src/creator/src/utils/creatorUtils.ts +46 -46
  44. package/src/creator/src/utils/eventBus.ts +2 -2
  45. package/src/creator/src/utils/lib.ts +468 -468
  46. package/src/creator/src/utils/socket.ts +61 -61
  47. package/src/creator/src/utils/store.ts +222 -222
  48. package/src/creator/src/vite-env.d.ts +1 -1
  49. package/src/creator/vite.config.ts +13 -13
  50. package/src/creatorDist/assets/index-BfLyIQVh.js +10343 -10224
  51. package/src/managers/config/defaults.ts +49 -49
  52. package/src/managers/config/exercise.ts +364 -364
  53. package/src/managers/config/index.ts +775 -775
  54. package/src/managers/file.ts +236 -236
  55. package/src/managers/server/routes.ts +554 -554
  56. package/src/managers/session.ts +182 -182
  57. package/src/managers/telemetry.ts +188 -188
  58. package/src/models/action.ts +13 -13
  59. package/src/models/config-manager.ts +28 -28
  60. package/src/models/config.ts +106 -106
  61. package/src/models/creator.ts +47 -47
  62. package/src/models/exercise-obj.ts +30 -30
  63. package/src/models/session.ts +39 -39
  64. package/src/models/socket.ts +61 -61
  65. package/src/models/status.ts +16 -16
  66. package/src/ui/_app/app.css +1 -1
  67. package/src/ui/_app/app.js +400 -397
  68. package/src/ui/app.tar.gz +0 -0
  69. package/src/utils/BaseCommand.ts +56 -56
  70. package/src/utils/api.ts +53 -39
  71. package/src/utils/audit.ts +392 -392
  72. package/src/utils/checkNotInstalled.ts +267 -267
  73. package/src/utils/configBuilder.ts +82 -82
  74. package/src/utils/convertCreds.js +34 -34
  75. package/src/utils/creatorUtilities.ts +504 -504
  76. package/src/utils/export/README.md +178 -0
  77. package/src/utils/export/epub.ts +400 -0
  78. package/src/utils/export/index.ts +3 -0
  79. package/src/utils/export/scorm.ts +121 -0
  80. package/src/utils/export/shared.ts +61 -0
  81. package/src/utils/export/types.ts +17 -0
  82. package/src/utils/incrementVersion.js +74 -74
  83. package/src/utils/misc.ts +58 -58
  84. package/src/utils/rigoActions.ts +500 -500
  85. package/src/utils/sidebarGenerator.ts +195 -195
  86. package/src/utils/templates/epub/epub.css +133 -0
  87. package/src/utils/templates/isolated/exercises/01-hello-world/README.es.md +26 -26
  88. package/src/utils/templates/isolated/exercises/01-hello-world/README.md +26 -26
  89. package/src/utils/templates/scorm/adlcp_rootv1p2.xsd +110 -0
  90. package/src/utils/templates/scorm/config/api.js +175 -0
  91. package/src/utils/templates/scorm/config/index.html +210 -0
  92. package/src/utils/templates/scorm/ims_xml.xsd +1 -0
  93. package/src/utils/templates/scorm/imscp_rootv1p1p2.xsd +345 -0
  94. package/src/utils/templates/scorm/imsmanifest.xml +38 -0
  95. package/src/utils/templates/scorm/imsmd_rootv1p2p1.xsd +573 -0
@@ -369,21 +369,21 @@ class AuditCommand extends SessionCommand_1.default {
369
369
  }
370
370
  }
371
371
  }
372
- AuditCommand.description = `learnpack audit is the command in charge of creating an auditory of the repository
373
- ...
374
- learnpack audit checks for the following information in a repository:
375
- 1. The configuration object has slug, repository and description. (Error)
376
- 2. The command learnpack clean has been run. (Error)
377
- 3. If a markdown or test file doesn't have any content. (Error)
378
- 4. The links are accessing to valid servers. (Error)
379
- 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)
380
- 6. The external images are working (If they are pointing to a valid server). (Error)
381
- 7. The exercises directory names are valid. (Error)
382
- 8. If an exercise doesn't have a README file. (Error)
383
- 9. The exercises array (Of the config file) has content. (Error)
384
- 10. The exercses have the same translations. (Warning)
385
- 11. The .gitignore file exists. (Warning)
386
- 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
372
+ AuditCommand.description = `learnpack audit is the command in charge of creating an auditory of the repository
373
+ ...
374
+ learnpack audit checks for the following information in a repository:
375
+ 1. The configuration object has slug, repository and description. (Error)
376
+ 2. The command learnpack clean has been run. (Error)
377
+ 3. If a markdown or test file doesn't have any content. (Error)
378
+ 4. The links are accessing to valid servers. (Error)
379
+ 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)
380
+ 6. The external images are working (If they are pointing to a valid server). (Error)
381
+ 7. The exercises directory names are valid. (Error)
382
+ 8. If an exercise doesn't have a README file. (Error)
383
+ 9. The exercises array (Of the config file) has content. (Error)
384
+ 10. The exercses have the same translations. (Warning)
385
+ 11. The .gitignore file exists. (Warning)
386
+ 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
387
387
  `;
388
388
  AuditCommand.flags = {
389
389
  strict: command_1.flags.boolean({
@@ -4,25 +4,25 @@ const command_1 = require("@oclif/command");
4
4
  const BaseCommand_1 = require("../utils/BaseCommand");
5
5
  const console_1 = require("../utils/console");
6
6
  const creatorUtilities_1 = require("../utils/creatorUtilities");
7
- const exampleMd = `# How to Install Node.js
8
-
9
- Node.js lets you run JavaScript outside a web browser.
10
-
11
- ## Step 1: Download Node.js
12
-
13
- Get the Node.js installer from the [official site](https://nodejs.org/en/download/).
14
-
15
- ## Step 2: Install Node.js
16
-
17
- Open the installer and follow the steps to finish.
18
-
19
- ## Step 3: Verify the Installation
20
-
21
- Open a terminal and type:
22
-
23
- \`\`\`bash
24
- node -v
25
- \`\`\`
7
+ const exampleMd = `# How to Install Node.js
8
+
9
+ Node.js lets you run JavaScript outside a web browser.
10
+
11
+ ## Step 1: Download Node.js
12
+
13
+ Get the Node.js installer from the [official site](https://nodejs.org/en/download/).
14
+
15
+ ## Step 2: Install Node.js
16
+
17
+ Open the installer and follow the steps to finish.
18
+
19
+ ## Step 3: Verify the Installation
20
+
21
+ Open a terminal and type:
22
+
23
+ \`\`\`bash
24
+ node -v
25
+ \`\`\`
26
26
  `;
27
27
  class BreakTokenCommand extends BaseCommand_1.default {
28
28
  async run() {
@@ -16,9 +16,9 @@ class CleanCommand extends SessionCommand_1.default {
16
16
  console_1.default.success("Package cleaned successfully, ready to publish");
17
17
  }
18
18
  }
19
- CleanCommand.description = `Clean the configuration object
20
- ...
21
- Extra documentation goes here
19
+ CleanCommand.description = `Clean the configuration object
20
+ ...
21
+ Extra documentation goes here
22
22
  `;
23
23
  CleanCommand.flags = {
24
24
  // name: flags.string({char: 'n', description: 'name to print'}),
@@ -107,9 +107,9 @@ const initializeInteractiveCreation = async (rigoToken, courseInfo) => {
107
107
  while (!isReady) {
108
108
  const spinner = (0, ora_1.default)("Thinking...").start();
109
109
  let wholeInfo = courseInfo;
110
- wholeInfo += `
111
- Current title: ${currentTitle}
112
- Current description: ${currentDescription}
110
+ wholeInfo += `
111
+ Current title: ${currentTitle}
112
+ Current description: ${currentDescription}
113
113
  `;
114
114
  // eslint-disable-next-line
115
115
  const res = await (0, rigoActions_1.interactiveCreation)(rigoToken, {
@@ -200,56 +200,56 @@ const handleAILogic = async (tutorialDir, packageInfo) => {
200
200
  fs.mkdirSync(rulesDir, { recursive: true });
201
201
  fs.writeFileSync(path.join(rulesDir, "airules.txt"), airules);
202
202
  }
203
- let packageContext = `
204
- \n
205
- Title: "${packageInfo.title.us}"
206
- Description: "${packageInfo.description.us}"
207
- Target Audience: "${targetAudience}"
208
- Estimated Duration: "${estimatedDuration} minutes"
209
-
203
+ let packageContext = `
204
+ \n
205
+ Title: "${packageInfo.title.us}"
206
+ Description: "${packageInfo.description.us}"
207
+ Target Audience: "${targetAudience}"
208
+ Estimated Duration: "${estimatedDuration} minutes"
209
+
210
210
  ${contentIndex ?
211
- `Content Index submitted by the user, use this to guide your creation. Keep in mind that your tutorial should contain these topics:
212
- ---
213
- ${contentIndex}
214
- ---
211
+ `Content Index submitted by the user, use this to guide your creation. Keep in mind that your tutorial should contain these topics:
212
+ ---
213
+ ${contentIndex}
214
+ ---
215
215
  ` :
216
- ""}
217
-
218
- This is the duration for each type of step, use it to estimate the number of steps to create:
216
+ ""}
217
+
218
+ This is the duration for each type of step, use it to estimate the number of steps to create:
219
219
  ${Object.entries(durationByKind)
220
220
  .map(([key, value]) => `${key}: ${value} minutes`)
221
- .join("\n")}
222
-
223
-
224
- Within the estimated duration, is possible to have the following activities:
225
- Format=
226
- Activity: Maximum number of steps for duration
227
-
228
- Estimated activities:
229
- ${JSON.stringify(estimateActivities(estimatedDuration))}
230
-
231
- You should create a tutorial that is engaging and fun to follow.
232
-
233
-
221
+ .join("\n")}
222
+
223
+
224
+ Within the estimated duration, is possible to have the following activities:
225
+ Format=
226
+ Activity: Maximum number of steps for duration
227
+
228
+ Estimated activities:
229
+ ${JSON.stringify(estimateActivities(estimatedDuration))}
230
+
231
+ You should create a tutorial that is engaging and fun to follow.
232
+
233
+
234
234
  ${airules ?
235
- `
236
- This is a list of rules you need to follow when creating the tutorial:
237
- ${airules}
235
+ `
236
+ This is a list of rules you need to follow when creating the tutorial:
237
+ ${airules}
238
238
  ` :
239
- ""}
239
+ ""}
240
240
  `;
241
241
  const { steps, title, description, duration, difficulty } = await initializeInteractiveCreation(rigoToken, packageContext);
242
242
  packageInfo.title.us = title;
243
243
  packageInfo.description.us = description;
244
244
  packageInfo.duration = duration;
245
245
  packageInfo.difficulty = difficulty;
246
- packageContext = `
247
- Title: "${title}"
248
- Description: "${description}"
249
- Target Audience: "${targetAudience}"
250
- List of exercises: ${steps.join(", ")}
251
-
252
- AI Rules: ${airules}
246
+ packageContext = `
247
+ Title: "${title}"
248
+ Description: "${description}"
249
+ Target Audience: "${targetAudience}"
250
+ List of exercises: ${steps.join(", ")}
251
+
252
+ AI Rules: ${airules}
253
253
  `;
254
254
  const exercisesDir = path.join(tutorialDir, "exercises");
255
255
  fs.ensureDirSync(exercisesDir);
@@ -14,9 +14,9 @@ class LogoutCommand extends SessionCommand_1.default {
14
14
  session_1.default.destroy();
15
15
  }
16
16
  }
17
- LogoutCommand.description = `Describe the command here
18
- ...
19
- Extra documentation goes here
17
+ LogoutCommand.description = `Describe the command here
18
+ ...
19
+ Extra documentation goes here
20
20
  `;
21
21
  LogoutCommand.flags = {
22
22
  // name: flags.string({char: 'n', description: 'name to print'}),
@@ -21,15 +21,7 @@ const misc_1 = require("../utils/misc");
21
21
  const creatorUtilities_1 = require("../utils/creatorUtilities");
22
22
  const uploadZipEndpont = api_1.RIGOBOT_HOST + "/v1/learnpack/upload";
23
23
  const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, learnpackDeployUrl, b64IndexReadme, all_translations = []) => {
24
- const categories = {
25
- en: 9,
26
- us: 9,
27
- es: 10,
28
- };
29
- let category = categories[selectedLang];
30
- if (!category) {
31
- category = 91;
32
- }
24
+ const category = "uncategorized";
33
25
  try {
34
26
  const user = await api_1.default.validateToken(sessionPayload.token);
35
27
  const slug = (0, creatorUtilities_1.slugify)(learnJson.title[selectedLang]).slice(0, 50);
@@ -59,6 +51,7 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
59
51
  const asset = await api_1.default.updateAsset(sessionPayload.token, slug, {
60
52
  learnpack_deploy_url: learnpackDeployUrl,
61
53
  title: learnJson.title[selectedLang],
54
+ category: category,
62
55
  description: learnJson.description[selectedLang],
63
56
  all_translations,
64
57
  });
@@ -216,7 +209,9 @@ class BuildCommand extends SessionCommand_1.default {
216
209
  this.copyDirectory(assetsDir, path.join(buildDir, ".learn", "assets"));
217
210
  }
218
211
  else {
219
- fs.mkdirSync(path.join(buildDir, ".learn", "assets"), { recursive: true });
212
+ fs.mkdirSync(path.join(buildDir, ".learn", "assets"), {
213
+ recursive: true,
214
+ });
220
215
  }
221
216
  // Copy .learn/_app directory files to the same level as config.json
222
217
  const appDir = path.join(process.cwd(), ".learn", "_app");
@@ -20,6 +20,7 @@ const file_1 = require("../managers/file");
20
20
  const fs = require("fs");
21
21
  const rigoActions_1 = require("../utils/rigoActions");
22
22
  const dotenv = require("dotenv");
23
+ // import { v4 as uuidv4 } from "uuid"
23
24
  const creatorUtilities_1 = require("../utils/creatorUtilities");
24
25
  // import { handleAssetCreation } from "./publish"
25
26
  const axios_1 = require("axios");
@@ -30,8 +31,11 @@ const configBuilder_1 = require("../utils/configBuilder");
30
31
  const creatorUtilities_2 = require("../utils/creatorUtilities");
31
32
  const sidebarGenerator_1 = require("../utils/sidebarGenerator");
32
33
  const publish_1 = require("./publish");
34
+ const export_1 = require("../utils/export");
33
35
  const frontMatter = require("front-matter");
34
36
  dotenv.config();
37
+ // Asegúrate de tener uuid instalado
38
+ // npm install uuid
35
39
  function findLast(array, predicate) {
36
40
  for (let i = array.length - 1; i >= 0; i--) {
37
41
  if (predicate(array[i]))
@@ -643,6 +647,7 @@ class ServeCommand extends SessionCommand_1.default {
643
647
  const { config, exercises } = await (0, configBuilder_1.buildConfig)(bucket, courseSlug);
644
648
  res.set("X-Creator-Web", "true");
645
649
  res.set("Access-Control-Expose-Headers", "X-Creator-Web");
650
+ await uploadFileToBucket(bucket, JSON.stringify({ config, exercises }), `courses/${courseSlug}/.learn/config.json`);
646
651
  res.json({ config, exercises });
647
652
  }
648
653
  catch (error) {
@@ -1212,7 +1217,6 @@ class ServeCommand extends SessionCommand_1.default {
1212
1217
  if (ytMatch) {
1213
1218
  const videoId = ytMatch[1];
1214
1219
  const resFromRigo = await axios_1.default.get(`${api_1.RIGOBOT_REALTIME_HOST}/actions/youtube-transcript/${videoId}`);
1215
- console.log("RES FROM RIGO", resFromRigo.data);
1216
1220
  const transcript = resFromRigo.data.transcript;
1217
1221
  // let meta: any = null
1218
1222
  // try {
@@ -1248,7 +1252,9 @@ class ServeCommand extends SessionCommand_1.default {
1248
1252
  }
1249
1253
  catch (error) {
1250
1254
  console.error("❌ /actions/fetch error:", error.message || error);
1251
- res.status(500).json({ error: error.message || "Failed to fetch link" });
1255
+ res
1256
+ .status(500)
1257
+ .json({ error: error.message || "Failed to fetch link" });
1252
1258
  }
1253
1259
  });
1254
1260
  app.delete("/packages/:slug", async (req, res) => {
@@ -1297,6 +1303,53 @@ class ServeCommand extends SessionCommand_1.default {
1297
1303
  res.status(500).json({ error: "Failed to fetch the resource" });
1298
1304
  }
1299
1305
  });
1306
+ app.get("/export/:course_slug/:format", async (req, res) => {
1307
+ const { course_slug, format } = req.params;
1308
+ const { language } = req.query;
1309
+ try {
1310
+ let outputPath;
1311
+ let filename;
1312
+ if (format === "scorm") {
1313
+ outputPath = await (0, export_1.exportToScorm)({
1314
+ courseSlug: course_slug,
1315
+ format: "scorm",
1316
+ bucket,
1317
+ outDir: path.join(__dirname, "../output/directory"),
1318
+ });
1319
+ filename = `${course_slug}-scorm.zip`;
1320
+ }
1321
+ else if (format === "epub") {
1322
+ outputPath = await (0, export_1.exportToEpub)({
1323
+ courseSlug: course_slug,
1324
+ format: "epub",
1325
+ bucket,
1326
+ outDir: path.join(__dirname, "../output/directory"),
1327
+ language: language || "en",
1328
+ });
1329
+ filename = `${course_slug}.epub`;
1330
+ }
1331
+ else {
1332
+ return res
1333
+ .status(400)
1334
+ .json({ error: "Invalid format. Supported formats: scorm, epub" });
1335
+ }
1336
+ // Send the file and clean up
1337
+ res.download(outputPath, filename, err => {
1338
+ if (err)
1339
+ console.error("Error sending file:", err);
1340
+ // Clean up the generated file
1341
+ if (fs.existsSync(outputPath)) {
1342
+ fs.unlinkSync(outputPath);
1343
+ }
1344
+ });
1345
+ }
1346
+ catch (error) {
1347
+ console.error("Export error:", error);
1348
+ res
1349
+ .status(500)
1350
+ .json({ error: "Export failed", details: error.message });
1351
+ }
1352
+ });
1300
1353
  server.listen(PORT, () => {
1301
1354
  console.log(`🚀 Creator UI server running at http://localhost:${PORT}/creator`);
1302
1355
  });