@learnpack/learnpack 5.0.284 → 5.0.285
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/lib/managers/session.js +2 -2
- package/lib/utils/export/epub.js +22 -5
- package/package.json +1 -1
- package/src/managers/session.ts +184 -182
- package/src/utils/export/epub.ts +29 -5
package/lib/managers/session.js
CHANGED
@@ -50,7 +50,7 @@ const Session = {
|
|
50
50
|
setPayload: async function (value) {
|
51
51
|
await this.initialize();
|
52
52
|
await storage.setItem("bc-payload", Object.assign({}, value));
|
53
|
-
console_1.default.debug("Payload successfuly found and set for " + value.
|
53
|
+
console_1.default.debug("Payload successfuly found and set for user " + value.user_id);
|
54
54
|
// TelemetryManager.setStudent({
|
55
55
|
// user_id: value.user_id.toString(),
|
56
56
|
// email: value.email,
|
@@ -124,7 +124,7 @@ const Session = {
|
|
124
124
|
}
|
125
125
|
this.token = token;
|
126
126
|
if (payload && (await this.setPayload(payload))) {
|
127
|
-
console_1.default.success(`Successfully logged in as ${payload.
|
127
|
+
console_1.default.success(`Successfully logged in as user ${payload.user_id}`);
|
128
128
|
}
|
129
129
|
},
|
130
130
|
destroy: async function () {
|
package/lib/utils/export/epub.js
CHANGED
@@ -8,13 +8,30 @@ const rimraf = require("rimraf");
|
|
8
8
|
const uuid_1 = require("uuid");
|
9
9
|
const child_process_1 = require("child_process");
|
10
10
|
const shared_1 = require("./shared");
|
11
|
-
const
|
11
|
+
const getLocalizedText = (language) => {
|
12
|
+
const translations = {
|
13
|
+
en: "Write your answer here",
|
14
|
+
es: "Escribe tu respuesta aquí",
|
15
|
+
fr: "Écrivez votre réponse ici",
|
16
|
+
de: "Schreiben Sie Ihre Antwort hier",
|
17
|
+
it: "Scrivi la tua risposta qui",
|
18
|
+
pt: "Escreva sua resposta aqui",
|
19
|
+
ja: "ここに答えを書いてください",
|
20
|
+
ko: "여기에 답을 작성하세요",
|
21
|
+
zh: "在这里写下你的答案",
|
22
|
+
ar: "اكتب إجابتك هنا",
|
23
|
+
ru: "Напишите свой ответ здесь",
|
24
|
+
};
|
25
|
+
return translations[language] || translations.en;
|
26
|
+
};
|
27
|
+
const replaceQuestionsFromMarkdown = (markdown, language = "en") => {
|
12
28
|
// Search for all code blocks like this (No breaking lines sensitive)
|
13
29
|
// ```question some parameters
|
14
30
|
// some text
|
15
31
|
// ```
|
16
32
|
const questionRegex = /```question([\s\S]*?)```/g;
|
17
|
-
|
33
|
+
const localizedText = getLocalizedText(language);
|
34
|
+
return markdown.replace(questionRegex, `<div type="text" class="open-question">${localizedText}</div>`);
|
18
35
|
};
|
19
36
|
const replaceMermaidFromMarkdown = (markdown) => {
|
20
37
|
// Search for all code blocks like this (No breaking lines sensitive)
|
@@ -186,7 +203,7 @@ async function processExercises(exercisesDir, outputDir, language = "en", learnA
|
|
186
203
|
}
|
187
204
|
// Process markdown content and save to output directory
|
188
205
|
console.log("Processing markdown file:", relativeFilePath);
|
189
|
-
const processedContent = processMarkdownContent(fullPath, learnAssetsDir);
|
206
|
+
const processedContent = processMarkdownContent(fullPath, learnAssetsDir, language);
|
190
207
|
const processedMarkdownPath = path.join(outputDir, relativeFilePath.replace(/\.(md|markdown)$/, ".processed.md"));
|
191
208
|
console.log("Processed markdown output path:", processedMarkdownPath);
|
192
209
|
mkdirp.sync(path.dirname(processedMarkdownPath));
|
@@ -213,7 +230,7 @@ async function processExercises(exercisesDir, outputDir, language = "en", learnA
|
|
213
230
|
return processedMarkdownFiles;
|
214
231
|
}
|
215
232
|
// Process markdown content for EPUB conversion
|
216
|
-
function processMarkdownContent(markdownPath, learnAssetsDir) {
|
233
|
+
function processMarkdownContent(markdownPath, learnAssetsDir, language = "en") {
|
217
234
|
let markdownContent = fs.readFileSync(markdownPath, "utf-8");
|
218
235
|
if (learnAssetsDir && fs.existsSync(learnAssetsDir)) {
|
219
236
|
// Find all image references in markdown
|
@@ -248,7 +265,7 @@ function processMarkdownContent(markdownPath, learnAssetsDir) {
|
|
248
265
|
}
|
249
266
|
}
|
250
267
|
}
|
251
|
-
markdownContent = replaceQuestionsFromMarkdown(markdownContent);
|
268
|
+
markdownContent = replaceQuestionsFromMarkdown(markdownContent, language);
|
252
269
|
markdownContent = replaceMermaidFromMarkdown(markdownContent);
|
253
270
|
markdownContent = cleanQuizOptions(markdownContent);
|
254
271
|
return markdownContent;
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@learnpack/learnpack",
|
3
3
|
"description": "Seamlessly build, sell and/or take interactive & auto-graded tutorials, start learning now or build a new tutorial to your audience.",
|
4
|
-
"version": "5.0.
|
4
|
+
"version": "5.0.285",
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
6
6
|
"contributors": [
|
7
7
|
{
|
package/src/managers/session.ts
CHANGED
@@ -1,182 +1,184 @@
|
|
1
|
-
import Console from "../utils/console"
|
2
|
-
import api from "../utils/api"
|
3
|
-
|
4
|
-
import v from "validator"
|
5
|
-
import { ValidationError, InternalError } from "../utils/errors"
|
6
|
-
|
7
|
-
import * as fs from "fs"
|
8
|
-
import cli from "cli-ux"
|
9
|
-
import * as storage from "node-persist"
|
10
|
-
|
11
|
-
import { IPayload, ISession, IStartProps } from "../models/session"
|
12
|
-
import { IConfigObj } from "../models/config"
|
13
|
-
import TelemetryManager from "./telemetry"
|
14
|
-
import * as path from "path"
|
15
|
-
import * as os from "os"
|
16
|
-
|
17
|
-
const GLOBAL_SESSION_PATH = path.join(os.homedir(), ".learnpack-session")
|
18
|
-
|
19
|
-
const Session: ISession = {
|
20
|
-
sessionStarted: false,
|
21
|
-
token: null,
|
22
|
-
config: null,
|
23
|
-
currentCohort: null,
|
24
|
-
initialize: async function () {
|
25
|
-
if (!this.sessionStarted) {
|
26
|
-
// Use a system-wide path instead of config.dirPath
|
27
|
-
if (!fs.existsSync(GLOBAL_SESSION_PATH)) {
|
28
|
-
fs.mkdirSync(GLOBAL_SESSION_PATH, { recursive: true })
|
29
|
-
}
|
30
|
-
|
31
|
-
await storage.init({ dir: GLOBAL_SESSION_PATH })
|
32
|
-
this.sessionStarted = true
|
33
|
-
}
|
34
|
-
|
35
|
-
return true
|
36
|
-
},
|
37
|
-
|
38
|
-
setRigoToken: async function (token: string) {
|
39
|
-
await this.initialize()
|
40
|
-
const payload = await storage.getItem("bc-payload")
|
41
|
-
await storage.setItem("bc-payload", {
|
42
|
-
...payload,
|
43
|
-
rigobot: { key: token },
|
44
|
-
})
|
45
|
-
Console.debug("Rigobot token successfuly set")
|
46
|
-
return true
|
47
|
-
},
|
48
|
-
setTabHash: async function (hash: string) {
|
49
|
-
await this.initialize()
|
50
|
-
const payload = await storage.getItem("bc-payload")
|
51
|
-
await storage.setItem("bc-payload", {
|
52
|
-
...payload,
|
53
|
-
tabHash: hash,
|
54
|
-
})
|
55
|
-
Console.debug("tabHash successfuly set")
|
56
|
-
return true
|
57
|
-
},
|
58
|
-
setSessionKey: async function (value: string) {
|
59
|
-
await this.initialize()
|
60
|
-
const payload = await storage.getItem("bc-payload")
|
61
|
-
await storage.setItem("bc-payload", {
|
62
|
-
...payload,
|
63
|
-
sessionKey: value,
|
64
|
-
})
|
65
|
-
Console.debug("sessionKey successfuly set")
|
66
|
-
return true
|
67
|
-
},
|
68
|
-
setPayload: async function (value: IPayload) {
|
69
|
-
await this.initialize()
|
70
|
-
await storage.setItem("bc-payload", { ...value })
|
71
|
-
Console.debug(
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
//
|
76
|
-
//
|
77
|
-
//
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
}
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
}
|
181
|
-
|
182
|
-
|
1
|
+
import Console from "../utils/console"
|
2
|
+
import api from "../utils/api"
|
3
|
+
|
4
|
+
import v from "validator"
|
5
|
+
import { ValidationError, InternalError } from "../utils/errors"
|
6
|
+
|
7
|
+
import * as fs from "fs"
|
8
|
+
import cli from "cli-ux"
|
9
|
+
import * as storage from "node-persist"
|
10
|
+
|
11
|
+
import { IPayload, ISession, IStartProps } from "../models/session"
|
12
|
+
import { IConfigObj } from "../models/config"
|
13
|
+
import TelemetryManager from "./telemetry"
|
14
|
+
import * as path from "path"
|
15
|
+
import * as os from "os"
|
16
|
+
|
17
|
+
const GLOBAL_SESSION_PATH = path.join(os.homedir(), ".learnpack-session")
|
18
|
+
|
19
|
+
const Session: ISession = {
|
20
|
+
sessionStarted: false,
|
21
|
+
token: null,
|
22
|
+
config: null,
|
23
|
+
currentCohort: null,
|
24
|
+
initialize: async function () {
|
25
|
+
if (!this.sessionStarted) {
|
26
|
+
// Use a system-wide path instead of config.dirPath
|
27
|
+
if (!fs.existsSync(GLOBAL_SESSION_PATH)) {
|
28
|
+
fs.mkdirSync(GLOBAL_SESSION_PATH, { recursive: true })
|
29
|
+
}
|
30
|
+
|
31
|
+
await storage.init({ dir: GLOBAL_SESSION_PATH })
|
32
|
+
this.sessionStarted = true
|
33
|
+
}
|
34
|
+
|
35
|
+
return true
|
36
|
+
},
|
37
|
+
|
38
|
+
setRigoToken: async function (token: string) {
|
39
|
+
await this.initialize()
|
40
|
+
const payload = await storage.getItem("bc-payload")
|
41
|
+
await storage.setItem("bc-payload", {
|
42
|
+
...payload,
|
43
|
+
rigobot: { key: token },
|
44
|
+
})
|
45
|
+
Console.debug("Rigobot token successfuly set")
|
46
|
+
return true
|
47
|
+
},
|
48
|
+
setTabHash: async function (hash: string) {
|
49
|
+
await this.initialize()
|
50
|
+
const payload = await storage.getItem("bc-payload")
|
51
|
+
await storage.setItem("bc-payload", {
|
52
|
+
...payload,
|
53
|
+
tabHash: hash,
|
54
|
+
})
|
55
|
+
Console.debug("tabHash successfuly set")
|
56
|
+
return true
|
57
|
+
},
|
58
|
+
setSessionKey: async function (value: string) {
|
59
|
+
await this.initialize()
|
60
|
+
const payload = await storage.getItem("bc-payload")
|
61
|
+
await storage.setItem("bc-payload", {
|
62
|
+
...payload,
|
63
|
+
sessionKey: value,
|
64
|
+
})
|
65
|
+
Console.debug("sessionKey successfuly set")
|
66
|
+
return true
|
67
|
+
},
|
68
|
+
setPayload: async function (value: IPayload) {
|
69
|
+
await this.initialize()
|
70
|
+
await storage.setItem("bc-payload", { ...value })
|
71
|
+
Console.debug(
|
72
|
+
"Payload successfuly found and set for user " + value.user_id
|
73
|
+
)
|
74
|
+
|
75
|
+
// TelemetryManager.setStudent({
|
76
|
+
// user_id: value.user_id.toString(),
|
77
|
+
// email: value.email,
|
78
|
+
// token: value.token,
|
79
|
+
// })
|
80
|
+
|
81
|
+
return true
|
82
|
+
},
|
83
|
+
getPayload: async function () {
|
84
|
+
await this.initialize()
|
85
|
+
let payload = null
|
86
|
+
try {
|
87
|
+
payload = await storage.getItem("bc-payload")
|
88
|
+
} catch {
|
89
|
+
Console.debug("Error retriving session payload")
|
90
|
+
}
|
91
|
+
|
92
|
+
return payload
|
93
|
+
},
|
94
|
+
isActive: function () {
|
95
|
+
return !!this.token
|
96
|
+
},
|
97
|
+
get: async function (configObj?: IConfigObj) {
|
98
|
+
if (configObj && configObj.config) {
|
99
|
+
this.config = configObj.config
|
100
|
+
}
|
101
|
+
|
102
|
+
await this.sync()
|
103
|
+
if (!this.isActive()) {
|
104
|
+
return null
|
105
|
+
}
|
106
|
+
|
107
|
+
const payload = await this.getPayload()
|
108
|
+
|
109
|
+
return {
|
110
|
+
payload,
|
111
|
+
token: this.token,
|
112
|
+
}
|
113
|
+
},
|
114
|
+
login: async function () {
|
115
|
+
const email = await cli.prompt("What is your email?")
|
116
|
+
if (!v.isEmail(email)) {
|
117
|
+
throw ValidationError("Invalid email")
|
118
|
+
}
|
119
|
+
|
120
|
+
const password = await cli.prompt("What is your password?", {
|
121
|
+
type: "hide",
|
122
|
+
})
|
123
|
+
|
124
|
+
const data = await api.login(email, password)
|
125
|
+
|
126
|
+
if (data) {
|
127
|
+
Console.debug("Login successfull")
|
128
|
+
// cli.log(data)
|
129
|
+
await this.start({ token: data.token, payload: data })
|
130
|
+
return data
|
131
|
+
}
|
132
|
+
},
|
133
|
+
loginWeb: async function (email, password) {
|
134
|
+
if (!v.isEmail(email)) {
|
135
|
+
throw ValidationError("Invalid email")
|
136
|
+
}
|
137
|
+
|
138
|
+
const data = await api.login(email, password)
|
139
|
+
if (data) {
|
140
|
+
this.start({ token: data.token, payload: data })
|
141
|
+
|
142
|
+
return data
|
143
|
+
}
|
144
|
+
},
|
145
|
+
sync: async function () {
|
146
|
+
const payload = await this.getPayload()
|
147
|
+
if (payload) {
|
148
|
+
this.token = payload.token
|
149
|
+
}
|
150
|
+
},
|
151
|
+
start: async function ({ token, payload = null }: IStartProps) {
|
152
|
+
if (!token) {
|
153
|
+
throw new Error("A token and email is needed to start a session")
|
154
|
+
}
|
155
|
+
|
156
|
+
this.token = token
|
157
|
+
|
158
|
+
if (payload && (await this.setPayload(payload))) {
|
159
|
+
Console.success(`Successfully logged in as user ${payload.user_id}`)
|
160
|
+
}
|
161
|
+
},
|
162
|
+
destroy: async function () {
|
163
|
+
if (!this.sessionStarted) {
|
164
|
+
await this.initialize()
|
165
|
+
}
|
166
|
+
|
167
|
+
await storage.clear()
|
168
|
+
this.token = null
|
169
|
+
Console.success("You have logged out")
|
170
|
+
},
|
171
|
+
breakToken: async function () {
|
172
|
+
const payload = await this.getPayload()
|
173
|
+
if (payload) {
|
174
|
+
this.token = "asdasdasdasd"
|
175
|
+
await storage.setItem("bc-payload", {
|
176
|
+
...payload,
|
177
|
+
token: "asdasdsad",
|
178
|
+
})
|
179
|
+
Console.success("Token broken successfully")
|
180
|
+
}
|
181
|
+
},
|
182
|
+
}
|
183
|
+
|
184
|
+
export default Session
|
package/src/utils/export/epub.ts
CHANGED
@@ -7,15 +7,37 @@ import { spawn } from "child_process";
|
|
7
7
|
import { ExportOptions, CourseMetadata, EpubMetadata } from "./types";
|
8
8
|
import { downloadS3Folder, getCourseMetadata } from "./shared";
|
9
9
|
|
10
|
-
const
|
10
|
+
const getLocalizedText = (language: string) => {
|
11
|
+
const translations = {
|
12
|
+
en: "Write your answer here",
|
13
|
+
es: "Escribe tu respuesta aquí",
|
14
|
+
fr: "Écrivez votre réponse ici",
|
15
|
+
de: "Schreiben Sie Ihre Antwort hier",
|
16
|
+
it: "Scrivi la tua risposta qui",
|
17
|
+
pt: "Escreva sua resposta aqui",
|
18
|
+
ja: "ここに答えを書いてください",
|
19
|
+
ko: "여기에 답을 작성하세요",
|
20
|
+
zh: "在这里写下你的答案",
|
21
|
+
ar: "اكتب إجابتك هنا",
|
22
|
+
ru: "Напишите свой ответ здесь",
|
23
|
+
};
|
24
|
+
|
25
|
+
return translations[language as keyof typeof translations] || translations.en;
|
26
|
+
};
|
27
|
+
|
28
|
+
const replaceQuestionsFromMarkdown = (
|
29
|
+
markdown: string,
|
30
|
+
language: string = "en"
|
31
|
+
) => {
|
11
32
|
// Search for all code blocks like this (No breaking lines sensitive)
|
12
33
|
// ```question some parameters
|
13
34
|
// some text
|
14
35
|
// ```
|
15
36
|
const questionRegex = /```question([\s\S]*?)```/g;
|
37
|
+
const localizedText = getLocalizedText(language);
|
16
38
|
return markdown.replace(
|
17
39
|
questionRegex,
|
18
|
-
|
40
|
+
`<div type="text" class="open-question">${localizedText}</div>`
|
19
41
|
);
|
20
42
|
};
|
21
43
|
|
@@ -234,7 +256,8 @@ async function processExercises(
|
|
234
256
|
console.log("Processing markdown file:", relativeFilePath);
|
235
257
|
const processedContent = processMarkdownContent(
|
236
258
|
fullPath,
|
237
|
-
learnAssetsDir
|
259
|
+
learnAssetsDir,
|
260
|
+
language
|
238
261
|
);
|
239
262
|
|
240
263
|
const processedMarkdownPath = path.join(
|
@@ -288,7 +311,8 @@ async function processExercises(
|
|
288
311
|
// Process markdown content for EPUB conversion
|
289
312
|
function processMarkdownContent(
|
290
313
|
markdownPath: string,
|
291
|
-
learnAssetsDir?: string
|
314
|
+
learnAssetsDir?: string,
|
315
|
+
language: string = "en"
|
292
316
|
): string {
|
293
317
|
let markdownContent = fs.readFileSync(markdownPath, "utf-8");
|
294
318
|
|
@@ -335,7 +359,7 @@ function processMarkdownContent(
|
|
335
359
|
}
|
336
360
|
}
|
337
361
|
|
338
|
-
markdownContent = replaceQuestionsFromMarkdown(markdownContent);
|
362
|
+
markdownContent = replaceQuestionsFromMarkdown(markdownContent, language);
|
339
363
|
markdownContent = replaceMermaidFromMarkdown(markdownContent);
|
340
364
|
markdownContent = cleanQuizOptions(markdownContent);
|
341
365
|
|