@learnpack/learnpack 5.0.357 → 5.0.359
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/commands/publish.d.ts +0 -10
- package/lib/commands/publish.js +54 -170
- package/lib/commands/serve.js +145 -134
- package/lib/ui/static/og-image.png +0 -0
- package/lib/utils/api.d.ts +62 -15
- package/lib/utils/api.js +124 -45
- package/lib/utils/assetResolver.d.ts +116 -0
- package/lib/utils/assetResolver.js +268 -0
- package/lib/utils/assetSync.d.ts +48 -0
- package/lib/utils/assetSync.js +267 -0
- package/lib/utils/branding.d.ts +43 -0
- package/lib/utils/branding.js +75 -0
- package/lib/utils/creatorUtilities.d.ts +1 -0
- package/lib/utils/creatorUtilities.js +17 -1
- package/package.json +2 -2
- package/src/commands/publish.ts +63 -258
- package/src/commands/serve.ts +200 -162
- package/src/ui/_app/app.js +2072 -2072
- package/src/ui/app.tar.gz +0 -0
- package/src/ui/static/og-image.png +0 -0
- package/src/utils/api.ts +157 -42
- package/src/utils/assetResolver.ts +368 -0
- package/src/utils/assetSync.ts +343 -0
- package/src/utils/awsCredentials.ts +57 -57
- package/src/utils/branding.ts +78 -0
- package/src/utils/creatorUtilities.ts +16 -0
- package/src/utils/descriptions/publishStage.ts +394 -394
- package/src/utils/descriptions/resumePublication.ts +217 -217
- package/src/utils/descriptions/s3Storage.ts +214 -214
- package/src/utils/s3/packageManifestBackfill.ts +771 -771
|
@@ -1,14 +1,4 @@
|
|
|
1
1
|
import SessionCommand from "../utils/SessionCommand";
|
|
2
|
-
type ExistingAssetInfo = {
|
|
3
|
-
lang: string;
|
|
4
|
-
slug: string;
|
|
5
|
-
exists: boolean;
|
|
6
|
-
academyId?: number;
|
|
7
|
-
};
|
|
8
|
-
export declare const handleAssetCreation: (sessionPayload: {
|
|
9
|
-
token: string;
|
|
10
|
-
rigobotToken: string;
|
|
11
|
-
}, learnJson: any, selectedLang: string, learnpackDeployUrl: string, b64IndexReadme: string, academyId: number | undefined, learnpackId: number, preflightInfo?: ExistingAssetInfo, all_translations?: string[]) => Promise<any>;
|
|
12
2
|
declare class BuildCommand extends SessionCommand {
|
|
13
3
|
static description: string;
|
|
14
4
|
static flags: {
|
package/lib/commands/publish.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.handleAssetCreation = void 0;
|
|
4
3
|
/* eslint-disable arrow-parens */
|
|
5
4
|
/* eslint-disable unicorn/no-array-for-each */
|
|
6
5
|
const child_process_1 = require("child_process");
|
|
@@ -19,181 +18,56 @@ const prompts = require("prompts");
|
|
|
19
18
|
const rigoActions_1 = require("../utils/rigoActions");
|
|
20
19
|
const misc_1 = require("../utils/misc");
|
|
21
20
|
const creatorUtilities_1 = require("../utils/creatorUtilities");
|
|
21
|
+
const branding_1 = require("../utils/branding");
|
|
22
|
+
const assetResolver_1 = require("../utils/assetResolver");
|
|
23
|
+
const assetSync_1 = require("../utils/assetSync");
|
|
22
24
|
const uploadZipEndpont = api_1.RIGOBOT_HOST + "/v1/learnpack/upload";
|
|
23
|
-
const getAvailableLangs = (learnJson) => {
|
|
24
|
-
const langs = Object.keys((learnJson === null || learnJson === void 0 ? void 0 : learnJson.title) || {});
|
|
25
|
-
return langs.filter((l) => typeof l === "string" && l.length > 0);
|
|
26
|
-
};
|
|
27
25
|
const getDefaultLang = (learnJson) => {
|
|
28
|
-
const availableLangs = getAvailableLangs(learnJson);
|
|
26
|
+
const availableLangs = (0, assetResolver_1.getAvailableLangs)(learnJson);
|
|
29
27
|
if (availableLangs.length === 0)
|
|
30
28
|
return "en";
|
|
31
29
|
return availableLangs.includes("en") ? "en" : availableLangs[0];
|
|
32
30
|
};
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
return "";
|
|
36
|
-
const direct = translations[lang];
|
|
37
|
-
if (typeof direct === "string" && direct.trim().length > 0)
|
|
38
|
-
return direct;
|
|
39
|
-
for (const fb of fallbackLangs) {
|
|
40
|
-
const v = translations[fb];
|
|
41
|
-
if (typeof v === "string" && v.trim().length > 0)
|
|
42
|
-
return v;
|
|
43
|
-
}
|
|
44
|
-
const firstKey = Object.keys(translations)[0];
|
|
45
|
-
const first = firstKey ? translations[firstKey] : "";
|
|
46
|
-
return typeof first === "string" ? first : "";
|
|
47
|
-
};
|
|
48
|
-
const getExistingAssetsInfo = async (token, learnJson) => {
|
|
49
|
-
const availableLangs = getAvailableLangs(learnJson);
|
|
50
|
-
const results = [];
|
|
51
|
-
for (const lang of availableLangs) {
|
|
52
|
-
const assetTitle = getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, lang);
|
|
53
|
-
if (!assetTitle)
|
|
54
|
-
continue;
|
|
55
|
-
let slug = (0, creatorUtilities_1.slugify)(assetTitle).slice(0, 47);
|
|
56
|
-
slug = `${slug}-${lang}`;
|
|
57
|
-
// eslint-disable-next-line no-await-in-loop
|
|
58
|
-
const { exists, academyId } = await api_1.default.doesAssetExists(token, slug);
|
|
59
|
-
results.push({ lang, slug, exists, academyId });
|
|
60
|
-
}
|
|
61
|
-
return results;
|
|
62
|
-
};
|
|
63
|
-
const determinePublishAcademyMode = (existingAssets) => {
|
|
64
|
-
const academyIds = existingAssets
|
|
65
|
-
.filter((a) => a.exists && a.academyId !== undefined)
|
|
66
|
-
.map((a) => a.academyId);
|
|
67
|
-
const unique = [...new Set(academyIds)];
|
|
68
|
-
if (unique.length === 0)
|
|
69
|
-
return { type: "select" };
|
|
70
|
-
if (unique.length === 1)
|
|
71
|
-
return { type: "locked", academyId: unique[0] };
|
|
72
|
-
return { type: "conflict", academies: unique };
|
|
73
|
-
};
|
|
74
|
-
const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, learnpackDeployUrl, b64IndexReadme, academyId, learnpackId, preflightInfo, all_translations = []) => {
|
|
75
|
-
const category = "uncategorized";
|
|
31
|
+
const readReadmeFromDisk = async (lang) => {
|
|
32
|
+
const readmePath = path.join(process.cwd(), `README${(0, creatorUtilities_1.getReadmeExtension)(lang)}`);
|
|
76
33
|
try {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (!assetTitle) {
|
|
81
|
-
throw new Error(`Missing learn.json title for language "${selectedLang}"`);
|
|
82
|
-
}
|
|
83
|
-
let slug = (0, creatorUtilities_1.slugify)(assetTitle).slice(0, 47);
|
|
84
|
-
slug = `${slug}-${selectedLang}`;
|
|
85
|
-
// Use pre-flight info when available to avoid an extra GET request
|
|
86
|
-
const { exists, academyId: existingAcademyId } = preflightInfo !== null && preflightInfo !== void 0 ? preflightInfo : (await api_1.default.doesAssetExists(sessionPayload.token, slug));
|
|
87
|
-
// const technologies: unknown[] = Array.isArray(learnJson?.technologies) ?
|
|
88
|
-
// learnJson.technologies :
|
|
89
|
-
// []
|
|
90
|
-
if (!exists) {
|
|
91
|
-
console_1.default.info("Asset does not exist in this academy, creating it");
|
|
92
|
-
const assetPayload = {
|
|
93
|
-
slug: slug,
|
|
94
|
-
title: assetTitle,
|
|
95
|
-
lang: selectedLang,
|
|
96
|
-
graded: true,
|
|
97
|
-
description: assetDescription,
|
|
98
|
-
learnpack_deploy_url: learnpackDeployUrl,
|
|
99
|
-
// technologies: technologies.map((tech: unknown) =>
|
|
100
|
-
// String(tech).toLowerCase().replace(/\s+/g, "-")
|
|
101
|
-
// ),
|
|
102
|
-
technologies: [],
|
|
103
|
-
url: learnpackDeployUrl,
|
|
104
|
-
category: category,
|
|
105
|
-
owner: user.id,
|
|
106
|
-
author: user.id,
|
|
107
|
-
preview: learnJson.preview,
|
|
108
|
-
readme_raw: b64IndexReadme,
|
|
109
|
-
all_translations,
|
|
110
|
-
learnpack_id: learnpackId,
|
|
111
|
-
};
|
|
112
|
-
if (academyId !== undefined) {
|
|
113
|
-
assetPayload.academy_id = academyId;
|
|
114
|
-
}
|
|
115
|
-
const asset = await api_1.default.createAsset(sessionPayload.token, assetPayload);
|
|
116
|
-
try {
|
|
117
|
-
await api_1.default.updateRigoPackage(sessionPayload.rigobotToken.trim(), learnJson.slug, {
|
|
118
|
-
asset_id: asset.id,
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
catch (error) {
|
|
122
|
-
console_1.default.error("Error updating Rigo package:", error);
|
|
123
|
-
}
|
|
124
|
-
console_1.default.info("Asset created with id", asset.id);
|
|
125
|
-
return asset;
|
|
126
|
-
}
|
|
127
|
-
console_1.default.info("Asset exists, updating it");
|
|
128
|
-
const updatePayload = {
|
|
129
|
-
graded: true,
|
|
130
|
-
learnpack_deploy_url: learnpackDeployUrl,
|
|
131
|
-
title: assetTitle,
|
|
132
|
-
category: category,
|
|
133
|
-
description: assetDescription,
|
|
134
|
-
all_translations,
|
|
135
|
-
learnpack_id: learnpackId,
|
|
136
|
-
};
|
|
137
|
-
// Only set academy when the asset has none yet and the user selected one
|
|
138
|
-
if (existingAcademyId === undefined && academyId !== undefined) {
|
|
139
|
-
updatePayload.academy_id = academyId;
|
|
140
|
-
}
|
|
141
|
-
const asset = await api_1.default.updateAsset(sessionPayload.token, slug, updatePayload);
|
|
142
|
-
try {
|
|
143
|
-
await api_1.default.updateRigoPackage(sessionPayload.rigobotToken.trim(), learnJson.slug, {
|
|
144
|
-
asset_id: asset.id,
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
catch (error) {
|
|
148
|
-
console_1.default.error("Error updating Rigo package:", error);
|
|
149
|
-
}
|
|
150
|
-
console_1.default.info("Asset updated with id", asset.id);
|
|
151
|
-
return asset;
|
|
34
|
+
return fs.existsSync(readmePath) ?
|
|
35
|
+
fs.readFileSync(readmePath, "utf-8") :
|
|
36
|
+
"";
|
|
152
37
|
}
|
|
153
38
|
catch (error) {
|
|
154
|
-
console_1.default.error("Error
|
|
155
|
-
|
|
39
|
+
console_1.default.error("Error reading index readme:", error);
|
|
40
|
+
return "";
|
|
156
41
|
}
|
|
157
42
|
};
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const availableLangs = getAvailableLangs(learnJson);
|
|
161
|
-
if (availableLangs.length === 0) {
|
|
43
|
+
const syncAssetsFromDisk = async (sessionPayload, learnJson, deployUrl, selectedAcademyId, resolution) => {
|
|
44
|
+
if ((0, assetResolver_1.getAvailableLangs)(learnJson).length === 0) {
|
|
162
45
|
console_1.default.error("No languages found in learn.json.title. Add at least one language (e.g. title.en).");
|
|
163
46
|
return;
|
|
164
47
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
48
|
+
// The pre-upload resolution is reused unless the package did not exist in
|
|
49
|
+
// Rigobot yet (first publish): the upload has just created it.
|
|
50
|
+
const result = await (0, assetSync_1.syncPackageAssets)({
|
|
51
|
+
bcToken: sessionPayload.token,
|
|
52
|
+
rigoToken: sessionPayload.rigobotToken,
|
|
53
|
+
packageSlug: learnJson.slug,
|
|
54
|
+
learnJson,
|
|
55
|
+
deployUrl,
|
|
56
|
+
academyId: selectedAcademyId,
|
|
57
|
+
readReadme: readReadmeFromDisk,
|
|
58
|
+
resolution: resolution.learnpackId === null ? undefined : resolution,
|
|
59
|
+
});
|
|
60
|
+
for (const warning of result.warnings)
|
|
61
|
+
console_1.default.warning(warning);
|
|
62
|
+
for (const synced of result.synced) {
|
|
63
|
+
console_1.default.info(`Asset ${synced.action} for "${synced.lang}": #${synced.asset.id} ${synced.asset.slug}`);
|
|
169
64
|
}
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
let indexReadmeString = "";
|
|
174
|
-
try {
|
|
175
|
-
if (fs.existsSync(readmePath)) {
|
|
176
|
-
indexReadmeString = fs.readFileSync(readmePath, "utf-8");
|
|
177
|
-
}
|
|
65
|
+
for (const error of result.errors) {
|
|
66
|
+
if (error.kind === "lang_error") {
|
|
67
|
+
console_1.default.error(`Asset for "${error.lang}": ${error.error.detail}`);
|
|
178
68
|
}
|
|
179
|
-
|
|
180
|
-
console_1.default.error(
|
|
181
|
-
indexReadmeString = "";
|
|
182
|
-
}
|
|
183
|
-
const b64IndexReadme = Buffer.from(indexReadmeString).toString("base64");
|
|
184
|
-
const preflightInfo = existingAssetsInfo.find((a) => a.lang === lang);
|
|
185
|
-
try {
|
|
186
|
-
// eslint-disable-next-line no-await-in-loop
|
|
187
|
-
const asset = await (0, exports.handleAssetCreation)(sessionPayload, learnJson, lang, deployUrl, b64IndexReadme, selectedAcademyId, learnpackId, preflightInfo, all_translations);
|
|
188
|
-
if (!asset) {
|
|
189
|
-
console_1.default.debug("Could not create/update asset for lang", lang);
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
all_translations.push(asset.slug);
|
|
193
|
-
}
|
|
194
|
-
catch (error) {
|
|
195
|
-
console_1.default.error("Error creating asset for language", lang, error);
|
|
196
|
-
// Continue with other languages
|
|
69
|
+
else {
|
|
70
|
+
console_1.default.error(error.error.detail);
|
|
197
71
|
}
|
|
198
72
|
}
|
|
199
73
|
};
|
|
@@ -312,8 +186,19 @@ class BuildCommand extends SessionCommand_1.default {
|
|
|
312
186
|
process.exit(1);
|
|
313
187
|
}
|
|
314
188
|
console_1.default.info("Checking existing assets...");
|
|
315
|
-
const
|
|
316
|
-
|
|
189
|
+
const resolution = await (0, assetResolver_1.resolvePackageAssets)({
|
|
190
|
+
bcToken: sessionPayload.token,
|
|
191
|
+
rigoToken: rigoToken,
|
|
192
|
+
packageSlug: learnJson.slug,
|
|
193
|
+
learnJson,
|
|
194
|
+
});
|
|
195
|
+
for (const warning of resolution.warnings)
|
|
196
|
+
console_1.default.warning(warning);
|
|
197
|
+
if (resolution.packageState === "unknown") {
|
|
198
|
+
console_1.default.error(`${resolution.packageError}. Existing assets cannot be checked, so publishing now could create duplicates. Try again later.`);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
const academyMode = (0, assetResolver_1.determineAcademyMode)(resolution);
|
|
317
202
|
let selectedAcademyId;
|
|
318
203
|
if (academyMode.type === "conflict") {
|
|
319
204
|
console_1.default.warning(`Some of your assets are associated with different academies ` +
|
|
@@ -379,12 +264,11 @@ class BuildCommand extends SessionCommand_1.default {
|
|
|
379
264
|
if (fs.existsSync(indexHtmlPath)) {
|
|
380
265
|
let indexHtmlContent = fs.readFileSync(indexHtmlPath, "utf-8");
|
|
381
266
|
const selectedLang = getDefaultLang(learnJson);
|
|
382
|
-
const description = getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.description, selectedLang) ||
|
|
267
|
+
const description = (0, assetSync_1.getLocalizedValue)(learnJson === null || learnJson === void 0 ? void 0 : learnJson.description, selectedLang) ||
|
|
383
268
|
"LearnPack is awesome!";
|
|
384
|
-
const title = getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, selectedLang) ||
|
|
269
|
+
const title = (0, assetSync_1.getLocalizedValue)(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, selectedLang) ||
|
|
385
270
|
"LearnPack: Interactive Learning as a Service";
|
|
386
|
-
const previewUrl = learnJson.preview ||
|
|
387
|
-
"https://raw.githubusercontent.com/learnpack/ide/refs/heads/master/public/learnpack.svg";
|
|
271
|
+
const previewUrl = learnJson.preview || (0, branding_1.getFallbackPreviewUrl)();
|
|
388
272
|
// Replace placeholders and the <title>Old title </title> tag for a new tag with the title
|
|
389
273
|
indexHtmlContent = indexHtmlContent
|
|
390
274
|
.replace(/{{description}}/g, description)
|
|
@@ -402,9 +286,9 @@ class BuildCommand extends SessionCommand_1.default {
|
|
|
402
286
|
if (fs.existsSync(manifestPWA)) {
|
|
403
287
|
let manifestPWAContent = fs.readFileSync(manifestPWA, "utf-8");
|
|
404
288
|
const selectedLang = getDefaultLang(learnJson);
|
|
405
|
-
const courseTitle = getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, selectedLang) ||
|
|
406
|
-
getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, "us") ||
|
|
407
|
-
getLocalizedValue(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, "en") ||
|
|
289
|
+
const courseTitle = (0, assetSync_1.getLocalizedValue)(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, selectedLang) ||
|
|
290
|
+
(0, assetSync_1.getLocalizedValue)(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, "us") ||
|
|
291
|
+
(0, assetSync_1.getLocalizedValue)(learnJson === null || learnJson === void 0 ? void 0 : learnJson.title, "en") ||
|
|
408
292
|
"LearnPack";
|
|
409
293
|
manifestPWAContent = manifestPWAContent.replace("{{course_title}}", courseTitle);
|
|
410
294
|
const courseShortName = { answer: "testing-tutorial" };
|
|
@@ -461,7 +345,7 @@ class BuildCommand extends SessionCommand_1.default {
|
|
|
461
345
|
console.log(res.data);
|
|
462
346
|
fs.unlinkSync(zipFilePath);
|
|
463
347
|
this.removeDirectory(buildDir);
|
|
464
|
-
await
|
|
348
|
+
await syncAssetsFromDisk({ token: sessionPayload.token, rigobotToken: rigoToken }, learnJson, res.data.url, selectedAcademyId, resolution);
|
|
465
349
|
}
|
|
466
350
|
catch (error) {
|
|
467
351
|
if (axios_1.default.isAxiosError(error)) {
|