@learnpack/learnpack 5.0.250 → 5.0.254

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.
@@ -36,6 +36,7 @@ import * as dotenv from "dotenv"
36
36
  import {
37
37
  extractImagesFromMarkdown,
38
38
  getFilenameFromUrl,
39
+ getReadmeExtension,
39
40
  } from "../utils/creatorUtilities"
40
41
  // import { handleAssetCreation } from "./publish"
41
42
  import axios from "axios"
@@ -66,25 +67,18 @@ export const createLearnJson = (courseInfo: FormState) => {
66
67
  // console.log("courseInfo to create learn json", courseInfo)
67
68
 
68
69
  const expectedPreviewUrl = `https://${courseInfo.slug}.learn-pack.com/preview.png`
69
- console.log("Preview url in generated learn.json", expectedPreviewUrl)
70
70
 
71
71
  const language = courseInfo.language || "en"
72
72
 
73
73
  const learnJson = {
74
74
  slug: courseInfo.slug,
75
- title:
76
- language === "en" || language === "us" ?
77
- {
78
- us: courseInfo.title,
79
- } :
80
- {
81
- [language]: courseInfo.title,
82
- },
75
+ title: {
76
+ [language]: courseInfo.title,
77
+ },
83
78
  technologies: courseInfo.technologies || [],
84
79
  difficulty: "beginner",
85
80
  description: {
86
- [language === "en" || language === "us" ? "us" : language]:
87
- courseInfo.description,
81
+ [language]: courseInfo.description,
88
82
  },
89
83
  grading: "isolated",
90
84
  telemetry: {
@@ -140,12 +134,10 @@ const createInitialSidebar = async (
140
134
  slugs: string[],
141
135
  initialLanguage = "en"
142
136
  ) => {
143
- const language = initialLanguage === "en" ? "us" : initialLanguage
144
-
145
137
  const sidebar: Record<string, Record<string, string>> = {}
146
138
  for (const slug of slugs) {
147
139
  sidebar[slug] = {
148
- [language]: slug,
140
+ [initialLanguage]: slug,
149
141
  }
150
142
  }
151
143
 
@@ -208,9 +200,7 @@ const createMultiLangAsset = async (
208
200
  for (const lang of availableLangs) {
209
201
  // eslint-disable-next-line no-await-in-loop
210
202
  const indexReadme = await bucket.file(
211
- `courses/${courseSlug}/README.${
212
- lang === "us" || lang === "en" ? "md" : `${lang}.md`
213
- }`
203
+ `courses/${courseSlug}/README${getReadmeExtension(lang)}`
214
204
  )
215
205
  // eslint-disable-next-line no-await-in-loop
216
206
  const [indexReadmeContent] = await indexReadme.download()
@@ -227,6 +217,11 @@ const createMultiLangAsset = async (
227
217
  all_translations
228
218
  )
229
219
 
220
+ if (!asset) {
221
+ console.log("No se pudo crear el asset, saltando idioma", lang)
222
+ continue
223
+ }
224
+
230
225
  all_translations.push(asset.slug)
231
226
  }
232
227
  }
@@ -526,15 +521,10 @@ export default class ServeCommand extends SessionCommand {
526
521
  const body = req.body
527
522
 
528
523
  // Save the file as courses/courseSlug/README.md
529
- const filePath = `courses/${courseSlug}/README.${
530
- body.parsed.language_code === "us" ||
531
- body.parsed.language_code === "en" ?
532
- "md" :
533
- `${body.parsed.language_code}.md`
534
- }`
535
- console.log("Saving initial readme to", filePath)
524
+ const filePath = `courses/${courseSlug}/README${getReadmeExtension(
525
+ body.parsed.language_code
526
+ )}`
536
527
  await uploadFileToBucket(bucket, body.parsed.content, filePath)
537
- console.log("Initial readme saved to", filePath)
538
528
 
539
529
  res.json({ status: "SUCCESS" })
540
530
  }
@@ -725,6 +715,32 @@ export default class ServeCommand extends SessionCommand {
725
715
  res.json({ exists, file: file.name })
726
716
  })
727
717
 
718
+ app.post(
719
+ "/webhooks/:courseSlug/:exSlug/:lang/update-readme/:notificationId",
720
+ async (req, res) => {
721
+ console.log("RECEIVING README UPDATE WEBHOOK")
722
+ const { courseSlug, exSlug, lang, notificationId } = req.params
723
+ const body = req.body
724
+
725
+ console.log("BODY", body)
726
+
727
+ const readmePath = `courses/${courseSlug}/exercises/${exSlug}/README${getReadmeExtension(
728
+ lang
729
+ )}`
730
+
731
+ if (body.parsed.content) {
732
+ await uploadFileToBucket(bucket, body.parsed.content, readmePath)
733
+ }
734
+
735
+ emitToNotification(notificationId, {
736
+ status: "SUCCESS",
737
+ message: "Readme updated successfully",
738
+ })
739
+
740
+ res.json({ status: "SUCCESS" })
741
+ }
742
+ )
743
+
728
744
  app.get("/create", (req, res) => {
729
745
  console.log("GET /create")
730
746
  res.redirect("/creator/")
@@ -778,7 +794,9 @@ export default class ServeCommand extends SessionCommand {
778
794
 
779
795
  const { slug } = req.params
780
796
  const courseSlug = req.query.slug
781
- const lang = req.query.lang || "us"
797
+ const lang = req.query.lang || "en"
798
+
799
+ console.log("LANG", lang)
782
800
 
783
801
  if (!courseSlug) {
784
802
  return res.status(400).json({ error: "Missing courseSlug" })
@@ -796,30 +814,37 @@ export default class ServeCommand extends SessionCommand {
796
814
  return res.status(404).json({ error: "No README files found" })
797
815
  }
798
816
 
799
- const requestedFilename =
800
- lang === "us" || lang === "en" ?
801
- `${basePath}README.md` :
802
- `${basePath}README.${lang}.md`
817
+ const requestedFilename = `${basePath}README${getReadmeExtension(
818
+ lang as string
819
+ )}`
803
820
 
804
821
  let selectedFile = readmeFiles.find(f => f === requestedFilename)
805
822
 
806
823
  if (!selectedFile) {
824
+ console.log("No se encontro en ese idioma, devolviendo el primero")
807
825
  selectedFile = readmeFiles[0]
808
826
  console.warn(
809
827
  `Requested README not found for lang '${lang}', using '${selectedFile}'`
810
828
  )
811
829
  }
812
830
 
813
- let foundLang = "us"
831
+ let foundLang = "en"
814
832
  const match = selectedFile.match(/readme(?:\.([a-z]{2}))?\.md$/i)
815
833
  if (match && match[1]) {
816
834
  foundLang = match[1].toLowerCase()
817
835
  }
818
836
 
819
837
  const [contentBuffer] = await bucket.file(selectedFile).download()
820
- const { attributes, body } = frontMatter(contentBuffer.toString())
821
-
822
- res.send({ attributes, body, lang: foundLang })
838
+ try {
839
+ const { attributes, body } = frontMatter(contentBuffer.toString())
840
+ res.send({ attributes, body, lang: foundLang })
841
+ } catch {
842
+ res.status(200).json({
843
+ attributes: {},
844
+ body: contentBuffer.toString(),
845
+ lang: foundLang,
846
+ })
847
+ }
823
848
  } catch (error) {
824
849
  console.error(error)
825
850
  res.status(500).json({ error: "Internal server error" })
@@ -885,9 +910,9 @@ export default class ServeCommand extends SessionCommand {
885
910
 
886
911
  const courseSlug = query.slug
887
912
 
888
- const fileName = `courses/${courseSlug}/exercises/${title}/README${
889
- language === "us" || language === "en" ? "" : `.${language}`
890
- }.md`
913
+ const fileName = `courses/${courseSlug}/exercises/${title}/README${getReadmeExtension(
914
+ language
915
+ )}`
891
916
  const file = bucket.file(fileName)
892
917
  await file.save(readme)
893
918
  const created = await file.exists()
@@ -935,11 +960,9 @@ export default class ServeCommand extends SessionCommand {
935
960
  try {
936
961
  await Promise.all(
937
962
  exerciseSlugs.map(async (slug: string) => {
938
- const readmePath = `courses/${courseSlug}/exercises/${slug}/README${
939
- currentLanguage === "us" || currentLanguage === "en" ?
940
- "" :
941
- `.${currentLanguage}`
942
- }.md`
963
+ const readmePath = `courses/${courseSlug}/exercises/${slug}/README${getReadmeExtension(
964
+ currentLanguage
965
+ )}`
943
966
 
944
967
  const readme = await bucket.file(readmePath).download()
945
968
 
@@ -952,12 +975,9 @@ export default class ServeCommand extends SessionCommand {
952
975
  })
953
976
 
954
977
  const translatedReadme = await bucket.file(
955
- `courses/${courseSlug}/exercises/${slug}/README${
956
- response.parsed.output_language_code === "us" ||
957
- response.parsed.output_language_code === "en" ?
958
- "" :
959
- `.${response.parsed.output_language_code}`
960
- }.md`
978
+ `courses/${courseSlug}/exercises/${slug}/README${getReadmeExtension(
979
+ response.parsed.output_language_code
980
+ )}`
961
981
  )
962
982
  await translatedReadme.save(response.parsed.translation)
963
983
 
@@ -967,11 +987,6 @@ export default class ServeCommand extends SessionCommand {
967
987
  })
968
988
  )
969
989
 
970
- if (languageCodes.has("en")) {
971
- languageCodes.delete("en")
972
- languageCodes.add("us")
973
- }
974
-
975
990
  const currentLanguages = Object.keys(courseJson.title)
976
991
  for (const languageCode of currentLanguages) {
977
992
  if (languageCodes.has(languageCode)) {
@@ -980,8 +995,6 @@ export default class ServeCommand extends SessionCommand {
980
995
  }
981
996
 
982
997
  if ([...languageCodes].length > 0) {
983
- console.log("TRANSLATING COURSE METADATA", languageCodes)
984
-
985
998
  const translatedCourseMetadata = await Promise.all(
986
999
  [...languageCodes].map(async languageCode => {
987
1000
  const result = await translateCourseMetadata(rigoToken, {
@@ -1011,11 +1024,7 @@ export default class ServeCommand extends SessionCommand {
1011
1024
  )
1012
1025
 
1013
1026
  const previewReadme = await bucket.file(
1014
- `courses/${courseSlug}/README${
1015
- currentLanguage === "us" || currentLanguage === "en" ?
1016
- "" :
1017
- `.${currentLanguage}`
1018
- }.md`
1027
+ `courses/${courseSlug}/README${getReadmeExtension(currentLanguage)}`
1019
1028
  )
1020
1029
  const [previewReadmeContent] = await previewReadme.download()
1021
1030
  const previewReadmeContentString = previewReadmeContent.toString()
@@ -1033,11 +1042,9 @@ export default class ServeCommand extends SessionCommand {
1033
1042
 
1034
1043
  await bucket
1035
1044
  .file(
1036
- `courses/${courseSlug}/README${
1037
- languageCode === "us" || languageCode === "en" ?
1038
- "" :
1039
- `.${languageCode}`
1040
- }.md`
1045
+ `courses/${courseSlug}/README${getReadmeExtension(
1046
+ languageCode as string
1047
+ )}`
1041
1048
  )
1042
1049
  .save(translatedPreviewReadme.parsed.translation)
1043
1050
  })
@@ -1436,10 +1443,10 @@ export default class ServeCommand extends SessionCommand {
1436
1443
 
1437
1444
  const availableLangs = Object.keys(config.title)
1438
1445
 
1439
- let selectedLang = "us"
1446
+ let selectedLang = "en"
1440
1447
  let title = ""
1441
- if (availableLangs.includes("us")) {
1442
- title = config.title.us
1448
+ if (availableLangs.includes("en")) {
1449
+ title = config.title.en
1443
1450
  } else {
1444
1451
  // Select the first available lang
1445
1452
  title = config.title[availableLangs[0]]
@@ -174,7 +174,7 @@ function App() {
174
174
  let techs = technologies.filter((t) => t.lang === formState.language)
175
175
 
176
176
  if (techs.length === 0) {
177
- techs = technologies.filter((t) => t.lang === "us")
177
+ techs = technologies.filter((t) => t.lang === "en")
178
178
  }
179
179
 
180
180
  const res = await publicInteractiveCreation(
@@ -24,6 +24,8 @@ export const publicInteractiveCreation = async (
24
24
  "https://9cw5zmww-3000.use2.devtunnels.ms"
25
25
  }/notifications/${randomUID}`
26
26
 
27
+ console.log("WEBHOOK URL to send to Rigo", webhookUrl)
28
+
27
29
  const response = await axios.post(
28
30
  `${RIGOBOT_HOST}/v1/prompting${
29
31
  publicRequest ? "/public" : ""
@@ -18251,28 +18251,31 @@ const d2 = async (e) => {
18251
18251
  try {
18252
18252
  const l = Ku(15),
18253
18253
  u = `https://9cw5zmww-3000.use2.devtunnels.ms/notifications/${l}`
18254
- return {
18255
- res: (
18256
- await Pe.post(
18257
- `${Ds}/v1/prompting${r ? "/public" : ""}/completion/${
18258
- L1 ? "39" : "390"
18259
- }/`,
18260
- {
18261
- inputs: e,
18262
- include_purpose_objective: !0,
18263
- purpose_slug: n,
18264
- webhook_url: u,
18265
- },
18266
- {
18267
- headers: {
18268
- "Content-Type": "application/json",
18269
- Authorization: `Token ${t}`,
18254
+ return (
18255
+ console.log("WEBHOOK URL to send to Rigo", u),
18256
+ {
18257
+ res: (
18258
+ await Pe.post(
18259
+ `${Ds}/v1/prompting${r ? "/public" : ""}/completion/${
18260
+ L1 ? "39" : "390"
18261
+ }/`,
18262
+ {
18263
+ inputs: e,
18264
+ include_purpose_objective: !0,
18265
+ purpose_slug: n,
18266
+ webhook_url: u,
18270
18267
  },
18271
- }
18272
- )
18273
- ).data,
18274
- notificationId: l,
18275
- }
18268
+ {
18269
+ headers: {
18270
+ "Content-Type": "application/json",
18271
+ Authorization: `Token ${t}`,
18272
+ },
18273
+ }
18274
+ )
18275
+ ).data,
18276
+ notificationId: l,
18277
+ }
18278
+ )
18276
18279
  } catch (l) {
18277
18280
  const u = l
18278
18281
  return (
@@ -21192,7 +21195,7 @@ function K7() {
21192
21195
  y.rigoToken &&
21193
21196
  l({ ...y, rigoToken: "", bcToken: "", userId: "", user: null })
21194
21197
  let $ = T.filter((ee) => ee.lang === r.language)
21195
- $.length === 0 && ($ = T.filter((ee) => ee.lang === "us"))
21198
+ $.length === 0 && ($ = T.filter((ee) => ee.lang === "en"))
21196
21199
  const Q = await p2(
21197
21200
  {
21198
21201
  courseInfo: `${JSON.stringify(
@@ -10,7 +10,7 @@
10
10
  />
11
11
 
12
12
  <title>Learnpack Creator: Craft tutorials in seconds!</title>
13
- <script type="module" crossorigin src="/creator/assets/index-CZYzWk3G.js"></script>
13
+ <script type="module" crossorigin src="/creator/assets/index-CFM_Ypyi.js"></script>
14
14
  <link rel="stylesheet" crossorigin href="/creator/assets/index-DmpsXknz.css">
15
15
  </head>
16
16
  <body>
@@ -85,15 +85,12 @@ export const exercise = (
85
85
  // fs.writeFileSync(path + "/" + file, updatedMarkdown, "utf8")
86
86
  // }
87
87
 
88
- if (parts.length === 3)
89
- translations[parts[1]] = file
90
- else
91
- translations.us = file
88
+ if (parts.length === 3) translations[parts[1]] = file
89
+ else translations.us = file
92
90
  }
93
91
 
94
92
  // if the slug is a dot, it means there is not "exercises" folder, and its just a single README.md
95
- if (slug === ".")
96
- slug = "default-index"
93
+ if (slug === ".") slug = "default-index"
97
94
 
98
95
  const detected = detect(configObject, files)
99
96
 
@@ -120,16 +117,14 @@ slug = "default-index"
120
117
  exercises[position].done :
121
118
  false,
122
119
  getReadme: function (lang = null) {
123
- if (lang === "us")
124
- lang = null // <-- english is default, no need to append it to the file name
120
+ if (lang === "en" || lang === "us") lang = null // <-- english is default, no need to append it to the file name
125
121
 
126
122
  if (!fs.existsSync(`${this.path}/README${lang ? "." + lang : ""}.md`)) {
127
123
  Console.error(
128
124
  `Language ${lang} not found for exercise ${slug}, switching to default language`
129
125
  )
130
126
 
131
- if (lang)
132
- lang = null
127
+ if (lang) lang = null
133
128
 
134
129
  if (!fs.existsSync(`${this.path}/README${lang ? "." + lang : ""}.md`))
135
130
  throw new Error(
@@ -169,8 +164,7 @@ lang = null
169
164
 
170
165
  for (const _file of this.files) {
171
166
  const stats = fs.statSync(_file.path)
172
- if (stats.isDirectory() || _file.hidden)
173
- continue
167
+ if (stats.isDirectory() || _file.hidden) continue
174
168
  const fileContent = fs.readFileSync(_file.path)
175
169
  if (
176
170
  !fs.existsSync(`${config?.dirPath}/resets/${this.slug}/${_file.name}`)
@@ -217,8 +211,7 @@ continue
217
211
  getTestReport: function () {
218
212
  const _path = `${configObject?.confPath?.base}/reports/${this.slug}.json`
219
213
 
220
- if (!fs.existsSync(_path))
221
- return {}
214
+ if (!fs.existsSync(_path)) return {}
222
215
 
223
216
  const content = fs.readFileSync(_path)
224
217
  const data = JSON.parse(`${content}`)
@@ -230,8 +223,7 @@ return {}
230
223
  }
231
224
 
232
225
  export const validateExerciseDirectoryName = (str: string) => {
233
- if (str === "./")
234
- return true
226
+ if (str === "./") return true
235
227
  // TODO: Add nameValidationREgex from the config
236
228
  const regex = /^(\d{2,3}(\.\d{1,2})?-([\dA-Za-z]+(-|_)?)+)$/
237
229
  return regex.test(str)