@learnpack/learnpack 5.0.358 → 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 +52 -168
- package/lib/commands/serve.js +52 -116
- 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/creatorUtilities.d.ts +1 -0
- package/lib/utils/creatorUtilities.js +17 -1
- package/package.json +1 -1
- package/src/commands/publish.ts +61 -255
- package/src/commands/serve.ts +65 -140
- package/src/ui/_app/app.js +1784 -1784
- package/src/ui/app.tar.gz +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/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
package/src/commands/serve.ts
CHANGED
|
@@ -55,9 +55,7 @@ import api, {
|
|
|
55
55
|
RIGOBOT_HOST,
|
|
56
56
|
RIGOBOT_REALTIME_HOST,
|
|
57
57
|
listUserAcademies,
|
|
58
|
-
doesAssetExists,
|
|
59
58
|
type AssetSyncError,
|
|
60
|
-
resolveLearnpackPackageId,
|
|
61
59
|
} from "../utils/api"
|
|
62
60
|
import {
|
|
63
61
|
createUploadMiddleware,
|
|
@@ -66,7 +64,12 @@ import {
|
|
|
66
64
|
import { buildConfig } from "../utils/configBuilder"
|
|
67
65
|
import { checkReadability, slugify } from "../utils/creatorUtilities"
|
|
68
66
|
import { checkAndFixSidebarPure } from "../utils/sidebarGenerator"
|
|
69
|
-
import {
|
|
67
|
+
import {
|
|
68
|
+
determineAcademyMode,
|
|
69
|
+
hasPublishedAsset,
|
|
70
|
+
resolvePackageAssets,
|
|
71
|
+
} from "../utils/assetResolver"
|
|
72
|
+
import { syncPackageAssets } from "../utils/assetSync"
|
|
70
73
|
import {
|
|
71
74
|
FormState,
|
|
72
75
|
Lesson,
|
|
@@ -538,36 +541,6 @@ const cleanFormStateForSyllabus = (formState: FormState) => {
|
|
|
538
541
|
}
|
|
539
542
|
}
|
|
540
543
|
|
|
541
|
-
const getLocalizedValue = (
|
|
542
|
-
translations: Record<string, any> | undefined,
|
|
543
|
-
lang: string,
|
|
544
|
-
fallbackLangs: string[] = ["en", "us"]
|
|
545
|
-
): string => {
|
|
546
|
-
if (!translations || typeof translations !== "object") return ""
|
|
547
|
-
|
|
548
|
-
const direct = translations[lang]
|
|
549
|
-
if (typeof direct === "string" && direct.trim().length > 0) return direct
|
|
550
|
-
|
|
551
|
-
for (const fb of fallbackLangs) {
|
|
552
|
-
const v = translations[fb]
|
|
553
|
-
if (typeof v === "string" && v.trim().length > 0) return v
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
const firstKey = Object.keys(translations)[0]
|
|
557
|
-
const first = firstKey ? translations[firstKey] : ""
|
|
558
|
-
return typeof first === "string" ? first : ""
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
function assetSyncErrorDetail(err: any): string {
|
|
562
|
-
if (typeof err?.detail === "string") return err.detail
|
|
563
|
-
if (typeof err?.message === "string") return err.message
|
|
564
|
-
try {
|
|
565
|
-
return JSON.stringify(err)
|
|
566
|
-
} catch {
|
|
567
|
-
return String(err)
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
544
|
const createMultiLangAsset = async (
|
|
572
545
|
bucket: Bucket,
|
|
573
546
|
rigoToken: string,
|
|
@@ -577,85 +550,38 @@ const createMultiLangAsset = async (
|
|
|
577
550
|
deployUrl: string,
|
|
578
551
|
academyId?: number
|
|
579
552
|
): Promise<{ errors: AssetSyncError[] }> => {
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
return {
|
|
583
|
-
errors: [
|
|
584
|
-
{
|
|
585
|
-
kind: "package_error",
|
|
586
|
-
error: {
|
|
587
|
-
detail:
|
|
588
|
-
"Could not resolve Learnpack package id; assets not synced to Breathecode.",
|
|
589
|
-
},
|
|
590
|
-
},
|
|
591
|
-
],
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
const availableLangs = Object.keys(courseJson.title)
|
|
596
|
-
console.log("AVAILABLE LANGUAGES to upload asset", availableLangs)
|
|
597
|
-
|
|
598
|
-
const all_translations: string[] = []
|
|
599
|
-
const errors: AssetSyncError[] = []
|
|
600
|
-
|
|
601
|
-
for (const lang of availableLangs) {
|
|
602
|
-
// eslint-disable-next-line no-await-in-loop
|
|
603
|
-
const indexReadme = await bucket.file(
|
|
553
|
+
const readReadme = async (lang: string): Promise<string> => {
|
|
554
|
+
const indexReadme = bucket.file(
|
|
604
555
|
`courses/${courseSlug}/README${getReadmeExtension(lang)}`
|
|
605
556
|
)
|
|
606
|
-
let indexReadmeString = ""
|
|
607
557
|
try {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
indexReadmeString = indexReadmeContent.toString()
|
|
558
|
+
const [content] = await indexReadme.download()
|
|
559
|
+
return content.toString()
|
|
611
560
|
} catch (error) {
|
|
612
561
|
console.error("Error downloading index readme", error)
|
|
613
562
|
// TODO: Trigger generation of the index readme
|
|
614
|
-
|
|
563
|
+
return ""
|
|
615
564
|
}
|
|
565
|
+
}
|
|
616
566
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
b64IndexReadme,
|
|
627
|
-
academyId,
|
|
628
|
-
learnpackId,
|
|
629
|
-
undefined,
|
|
630
|
-
all_translations
|
|
631
|
-
)
|
|
632
|
-
|
|
633
|
-
if (!asset) {
|
|
634
|
-
errors.push({
|
|
635
|
-
kind: "lang_error",
|
|
636
|
-
lang,
|
|
637
|
-
error: { detail: "Failed to create asset" },
|
|
638
|
-
})
|
|
639
|
-
console.log("No se pudo crear el asset, saltando idioma", lang)
|
|
640
|
-
continue
|
|
641
|
-
}
|
|
567
|
+
const result = await syncPackageAssets({
|
|
568
|
+
bcToken,
|
|
569
|
+
rigoToken,
|
|
570
|
+
packageSlug: courseSlug,
|
|
571
|
+
learnJson: courseJson,
|
|
572
|
+
deployUrl,
|
|
573
|
+
academyId,
|
|
574
|
+
readReadme,
|
|
575
|
+
})
|
|
642
576
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
error
|
|
649
|
-
errors.push({
|
|
650
|
-
kind: "lang_error",
|
|
651
|
-
lang,
|
|
652
|
-
error: { detail: assetSyncErrorDetail(errorData) },
|
|
653
|
-
})
|
|
654
|
-
console.error(`Error creating asset for language ${lang}:`, error)
|
|
655
|
-
}
|
|
577
|
+
for (const warning of result.warnings) console.warn("[assets]", warning)
|
|
578
|
+
for (const synced of result.synced) {
|
|
579
|
+
console.log(
|
|
580
|
+
`[assets] ${synced.action} "${synced.lang}": #${synced.asset.id} ${synced.asset.slug}`
|
|
581
|
+
)
|
|
656
582
|
}
|
|
657
583
|
|
|
658
|
-
return { errors }
|
|
584
|
+
return { errors: result.errors }
|
|
659
585
|
}
|
|
660
586
|
|
|
661
587
|
const lessonCleaner = (lesson: Lesson) => {
|
|
@@ -5392,6 +5318,10 @@ class ServeCommand extends SessionCommand {
|
|
|
5392
5318
|
})
|
|
5393
5319
|
}
|
|
5394
5320
|
|
|
5321
|
+
// Older IDEs only send the Breathecode token; without the Rigobot
|
|
5322
|
+
// token the lookup falls back to the title-derived slugs.
|
|
5323
|
+
const rigoToken = req.header("x-rigo-token")
|
|
5324
|
+
|
|
5395
5325
|
const configFile = await bucket.file(
|
|
5396
5326
|
`courses/${slug}/.learn/config.json`
|
|
5397
5327
|
)
|
|
@@ -5399,56 +5329,51 @@ class ServeCommand extends SessionCommand {
|
|
|
5399
5329
|
const configJson = JSON.parse(configContent.toString())
|
|
5400
5330
|
const { config } = configJson
|
|
5401
5331
|
|
|
5402
|
-
const
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
const
|
|
5409
|
-
.
|
|
5410
|
-
|
|
5411
|
-
if (!assetTitle) return null
|
|
5412
|
-
const assetSlug = slugify(assetTitle).slice(0, 47)
|
|
5413
|
-
return `${assetSlug}-${lang}`
|
|
5414
|
-
})
|
|
5415
|
-
.filter((slug): slug is string => slug !== null)
|
|
5332
|
+
const resolution = await resolvePackageAssets({
|
|
5333
|
+
bcToken,
|
|
5334
|
+
rigoToken,
|
|
5335
|
+
packageSlug: slug,
|
|
5336
|
+
learnJson: config,
|
|
5337
|
+
})
|
|
5338
|
+
for (const warning of resolution.warnings) {
|
|
5339
|
+
console.warn("[package-academy]", warning)
|
|
5340
|
+
}
|
|
5416
5341
|
|
|
5417
|
-
const
|
|
5418
|
-
|
|
5419
|
-
|
|
5342
|
+
const isPublished = hasPublishedAsset(resolution)
|
|
5343
|
+
const academyMode = determineAcademyMode(resolution)
|
|
5344
|
+
const warnings = [...resolution.warnings]
|
|
5345
|
+
if (resolution.packageState === "unknown") {
|
|
5346
|
+
warnings.push(
|
|
5347
|
+
`${resolution.packageError}; existing assets could not be checked.`
|
|
5348
|
+
)
|
|
5349
|
+
}
|
|
5420
5350
|
|
|
5421
|
-
for (const
|
|
5422
|
-
if (
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5351
|
+
for (const [lang, entry] of resolution.byLang) {
|
|
5352
|
+
if (entry.status === "unknown") {
|
|
5353
|
+
warnings.push(`"${lang}": ${entry.error}`)
|
|
5354
|
+
} else if (entry.status === "ambiguous") {
|
|
5355
|
+
warnings.push(
|
|
5356
|
+
`"${lang}": ${
|
|
5357
|
+
entry.candidates.length
|
|
5358
|
+
} assets found (${entry.candidates
|
|
5359
|
+
.map(c => `#${c.id} ${c.slug}`)
|
|
5360
|
+
.join(", ")})`
|
|
5361
|
+
)
|
|
5427
5362
|
}
|
|
5428
5363
|
}
|
|
5429
5364
|
|
|
5430
|
-
const
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
let conflictAcademies: number[] | undefined
|
|
5436
|
-
|
|
5437
|
-
if (uniqueAcademies.length === 0) {
|
|
5438
|
-
mode = "select"
|
|
5439
|
-
} else if (uniqueAcademies.length === 1) {
|
|
5440
|
-
mode = "locked"
|
|
5441
|
-
lockedAcademyId = uniqueAcademies[0]
|
|
5442
|
-
} else {
|
|
5443
|
-
mode = "conflict"
|
|
5444
|
-
conflictAcademies = uniqueAcademies
|
|
5445
|
-
}
|
|
5365
|
+
const mode = academyMode.type
|
|
5366
|
+
const lockedAcademyId =
|
|
5367
|
+
academyMode.type === "locked" ? academyMode.academyId : undefined
|
|
5368
|
+
const conflictAcademies =
|
|
5369
|
+
academyMode.type === "conflict" ? academyMode.academies : undefined
|
|
5446
5370
|
|
|
5447
5371
|
return res.json({
|
|
5448
5372
|
isPublished,
|
|
5449
5373
|
mode,
|
|
5450
5374
|
lockedAcademyId,
|
|
5451
5375
|
conflictAcademies,
|
|
5376
|
+
warnings,
|
|
5452
5377
|
})
|
|
5453
5378
|
} catch (error) {
|
|
5454
5379
|
console.error("Error fetching package academy:", error)
|