@learnpack/learnpack 5.0.346 → 5.0.347

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.
@@ -8,7 +8,7 @@ type ExistingAssetInfo = {
8
8
  export declare const handleAssetCreation: (sessionPayload: {
9
9
  token: string;
10
10
  rigobotToken: string;
11
- }, learnJson: any, selectedLang: string, learnpackDeployUrl: string, b64IndexReadme: string, academyId: number | undefined, preflightInfo?: ExistingAssetInfo, all_translations?: string[]) => Promise<any>;
11
+ }, learnJson: any, selectedLang: string, learnpackDeployUrl: string, b64IndexReadme: string, academyId: number | undefined, learnpackId: number, preflightInfo?: ExistingAssetInfo, all_translations?: string[]) => Promise<any>;
12
12
  declare class BuildCommand extends SessionCommand {
13
13
  static description: string;
14
14
  static flags: {
@@ -71,7 +71,7 @@ const determinePublishAcademyMode = (existingAssets) => {
71
71
  return { type: "locked", academyId: unique[0] };
72
72
  return { type: "conflict", academies: unique };
73
73
  };
74
- const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, learnpackDeployUrl, b64IndexReadme, academyId, preflightInfo, all_translations = []) => {
74
+ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, learnpackDeployUrl, b64IndexReadme, academyId, learnpackId, preflightInfo, all_translations = []) => {
75
75
  const category = "uncategorized";
76
76
  try {
77
77
  const user = await api_1.default.validateToken(sessionPayload.token);
@@ -107,6 +107,7 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
107
107
  preview: learnJson.preview,
108
108
  readme_raw: b64IndexReadme,
109
109
  all_translations,
110
+ learnpack_id: learnpackId,
110
111
  };
111
112
  if (academyId !== undefined) {
112
113
  assetPayload.academy_id = academyId;
@@ -131,6 +132,7 @@ const handleAssetCreation = async (sessionPayload, learnJson, selectedLang, lear
131
132
  category: category,
132
133
  description: assetDescription,
133
134
  all_translations,
135
+ learnpack_id: learnpackId,
134
136
  };
135
137
  // Only set academy when the asset has none yet and the user selected one
136
138
  if (existingAcademyId === undefined && academyId !== undefined) {
@@ -160,6 +162,11 @@ const createMultiLangAssetFromDisk = async (sessionPayload, learnJson, deployUrl
160
162
  console_1.default.error("No languages found in learn.json.title. Add at least one language (e.g. title.en).");
161
163
  return;
162
164
  }
165
+ const learnpackId = await api_1.default.resolveLearnpackPackageId(sessionPayload.rigobotToken, learnJson.slug);
166
+ if (learnpackId === null) {
167
+ console_1.default.warning("Breathecode assets skipped: could not resolve Learnpack package id");
168
+ return;
169
+ }
163
170
  const all_translations = [];
164
171
  for (const lang of availableLangs) {
165
172
  const readmePath = path.join(process.cwd(), `README${(0, creatorUtilities_1.getReadmeExtension)(lang)}`);
@@ -177,7 +184,7 @@ const createMultiLangAssetFromDisk = async (sessionPayload, learnJson, deployUrl
177
184
  const preflightInfo = existingAssetsInfo.find((a) => a.lang === lang);
178
185
  try {
179
186
  // eslint-disable-next-line no-await-in-loop
180
- const asset = await (0, exports.handleAssetCreation)(sessionPayload, learnJson, lang, deployUrl, b64IndexReadme, selectedAcademyId, preflightInfo, all_translations);
187
+ const asset = await (0, exports.handleAssetCreation)(sessionPayload, learnJson, lang, deployUrl, b64IndexReadme, selectedAcademyId, learnpackId, preflightInfo, all_translations);
181
188
  if (!asset) {
182
189
  console_1.default.debug("Could not create/update asset for lang", lang);
183
190
  continue;
@@ -365,8 +365,33 @@ const getLocalizedValue = (translations, lang, fallbackLangs = ["en", "us"]) =>
365
365
  const first = firstKey ? translations[firstKey] : "";
366
366
  return typeof first === "string" ? first : "";
367
367
  };
368
+ function assetSyncErrorDetail(err) {
369
+ if (typeof (err === null || err === void 0 ? void 0 : err.detail) === "string")
370
+ return err.detail;
371
+ if (typeof (err === null || err === void 0 ? void 0 : err.message) === "string")
372
+ return err.message;
373
+ try {
374
+ return JSON.stringify(err);
375
+ }
376
+ catch (_a) {
377
+ return String(err);
378
+ }
379
+ }
368
380
  const createMultiLangAsset = async (bucket, rigoToken, bcToken, courseSlug, courseJson, deployUrl, academyId) => {
369
381
  var _a;
382
+ const learnpackId = await (0, api_1.resolveLearnpackPackageId)(rigoToken, courseSlug);
383
+ if (learnpackId === null) {
384
+ return {
385
+ errors: [
386
+ {
387
+ kind: "package_error",
388
+ error: {
389
+ detail: "Could not resolve Learnpack package id; assets not synced to Breathecode.",
390
+ },
391
+ },
392
+ ],
393
+ };
394
+ }
370
395
  const availableLangs = Object.keys(courseJson.title);
371
396
  console.log("AVAILABLE LANGUAGES to upload asset", availableLangs);
372
397
  const all_translations = [];
@@ -388,11 +413,12 @@ const createMultiLangAsset = async (bucket, rigoToken, bcToken, courseSlug, cour
388
413
  const b64IndexReadme = buffer_1.Buffer.from(indexReadmeString).toString("base64");
389
414
  try {
390
415
  // eslint-disable-next-line no-await-in-loop
391
- const asset = await (0, publish_1.handleAssetCreation)({ token: bcToken, rigobotToken: rigoToken.trim() }, courseJson, lang, deployUrl, b64IndexReadme, academyId, undefined, all_translations);
416
+ const asset = await (0, publish_1.handleAssetCreation)({ token: bcToken, rigobotToken: rigoToken.trim() }, courseJson, lang, deployUrl, b64IndexReadme, academyId, learnpackId, undefined, all_translations);
392
417
  if (!asset) {
393
418
  errors.push({
419
+ kind: "lang_error",
394
420
  lang,
395
- error: { detail: "Failed to create asset", status_code: 500 },
421
+ error: { detail: "Failed to create asset" },
396
422
  });
397
423
  console.log("No se pudo crear el asset, saltando idioma", lang);
398
424
  continue;
@@ -403,7 +429,11 @@ const createMultiLangAsset = async (bucket, rigoToken, bcToken, courseSlug, cour
403
429
  const errorData = error && typeof error === "object" && "response" in error ?
404
430
  ((_a = error.response) === null || _a === void 0 ? void 0 : _a.data) || error :
405
431
  error;
406
- errors.push({ lang, error: errorData });
432
+ errors.push({
433
+ kind: "lang_error",
434
+ lang,
435
+ error: { detail: assetSyncErrorDetail(errorData) },
436
+ });
407
437
  console.error(`Error creating asset for language ${lang}:`, error);
408
438
  }
409
439
  }
@@ -3913,20 +3943,65 @@ class ServeCommand extends SessionCommand_1.default {
3913
3943
  const output = fs.createWriteStream(zipPath);
3914
3944
  const archive = archiver("zip", { zlib: { level: 9 } });
3915
3945
  output.on("close", async () => {
3916
- // 10) Subir ZIP a RigoBot
3917
- const form = new FormData();
3918
- form.append("file", fs.createReadStream(zipPath));
3919
- form.append("config", JSON.stringify(config));
3920
- const rigoRes = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/learnpack/upload`, form, {
3921
- headers: Object.assign(Object.assign({}, form.getHeaders()), { Authorization: "Token " + rigoToken.trim() }),
3922
- });
3923
- const assetResults = await createMultiLangAsset(bucket, rigoToken, bcToken, slug, fullConfig.config, rigoRes.data.url, academyId);
3924
- rimraf.sync(tmpRoot);
3925
- console.log("RigoRes", rigoRes.data);
3926
- return res.json({
3927
- url: rigoRes.data.url,
3928
- errors: assetResults.errors,
3929
- });
3946
+ let rigoPublishUrl;
3947
+ try {
3948
+ // 10) Subir ZIP a RigoBot
3949
+ const form = new FormData();
3950
+ form.append("file", fs.createReadStream(zipPath));
3951
+ form.append("config", JSON.stringify(config));
3952
+ const rigoRes = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/learnpack/upload`, form, {
3953
+ headers: Object.assign(Object.assign({}, form.getHeaders()), { Authorization: "Token " + rigoToken.trim() }),
3954
+ });
3955
+ rigoPublishUrl = rigoRes.data.url;
3956
+ let errors;
3957
+ try {
3958
+ const assetResults = await createMultiLangAsset(bucket, rigoToken, bcToken, slug, fullConfig.config, rigoRes.data.url, academyId);
3959
+ errors = assetResults.errors;
3960
+ }
3961
+ catch (error) {
3962
+ console.error("Asset sync failed unexpectedly:", error);
3963
+ errors = [
3964
+ {
3965
+ kind: "package_error",
3966
+ error: { detail: "Asset sync failed unexpectedly." },
3967
+ },
3968
+ ];
3969
+ }
3970
+ if (res.headersSent)
3971
+ return;
3972
+ console.log("RigoRes", rigoRes.data);
3973
+ res.json({
3974
+ url: rigoPublishUrl,
3975
+ errors,
3976
+ });
3977
+ }
3978
+ catch (error) {
3979
+ console.error(error);
3980
+ if (res.headersSent)
3981
+ return;
3982
+ if (rigoPublishUrl !== undefined) {
3983
+ res.json({
3984
+ url: rigoPublishUrl,
3985
+ errors: [
3986
+ {
3987
+ kind: "package_error",
3988
+ error: { detail: "Asset sync failed unexpectedly." },
3989
+ },
3990
+ ],
3991
+ });
3992
+ }
3993
+ else {
3994
+ res.status(500).json({ error: error.message });
3995
+ }
3996
+ }
3997
+ finally {
3998
+ try {
3999
+ rimraf.sync(tmpRoot);
4000
+ }
4001
+ catch (error) {
4002
+ console.error("rimraf tmpRoot:", error);
4003
+ }
4004
+ }
3930
4005
  });
3931
4006
  archive.on("error", err => {
3932
4007
  console.error("ZIP Error:", err);
@@ -11,6 +11,26 @@ export interface TAcademy {
11
11
  }
12
12
  export declare const listUserAcademies: (breathecodeToken: string) => Promise<TAcademy[]>;
13
13
  export declare const validateToken: (token: string) => Promise<any>;
14
+ /** keep in sync with ide/src/components/Creator/PublishButton.tsx AssetSyncError */
15
+ export type AssetSyncError = {
16
+ kind: "lang_error";
17
+ lang: string;
18
+ error: {
19
+ detail: string;
20
+ };
21
+ } | {
22
+ kind: "package_error";
23
+ error: {
24
+ detail: string;
25
+ };
26
+ };
27
+ /**
28
+ * GET Rigobot package by slug after a successful upload. Does not throw.
29
+ * @param rigoToken Rigobot API token (Bearer-style `Token` header value).
30
+ * @param courseSlug Learnpack package slug used in the Rigobot URL path.
31
+ * @returns Resolved numeric package id from Rigobot, or `null` if the request fails, the id is missing, or it is not a finite integer.
32
+ */
33
+ export declare function resolveLearnpackPackageId(rigoToken: string, courseSlug: string): Promise<number | null>;
14
34
  type TAssetMissing = {
15
35
  slug: string;
16
36
  title: string;
@@ -27,6 +47,7 @@ type TAssetMissing = {
27
47
  readme_raw: string;
28
48
  all_translations: string[];
29
49
  academy_id?: number;
50
+ learnpack_id: number;
30
51
  };
31
52
  export declare const createAsset: (token: string, asset: TAssetMissing) => Promise<any>;
32
53
  export declare const doesAssetExists: (token: string, assetSlug: string) => Promise<{
@@ -59,7 +80,10 @@ declare const _default: {
59
80
  exists: boolean;
60
81
  academyId?: number;
61
82
  }>;
62
- updateAsset: (token: string, assetSlug: string, asset: Partial<TAssetMissing>) => Promise<any>;
83
+ updateAsset: (token: string, assetSlug: string, asset: Partial<TAssetMissing> & {
84
+ learnpack_id: number;
85
+ }) => Promise<any>;
86
+ resolveLearnpackPackageId: typeof resolveLearnpackPackageId;
63
87
  getCategories: (token: string) => Promise<any>;
64
88
  updateRigoPackage: (token: string, slug: string, updates: {
65
89
  asset_id?: number;
package/lib/utils/api.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getCurrentTechnologies = exports.fetchTechnologies = exports.doesAssetExists = exports.createAsset = exports.validateToken = exports.listUserAcademies = exports.getConsumable = exports.countConsumables = exports.RIGOBOT_REALTIME_HOST = exports.RIGOBOT_HOST = void 0;
4
+ exports.resolveLearnpackPackageId = resolveLearnpackPackageId;
4
5
  const console_1 = require("../utils/console");
5
6
  const storage = require("node-persist");
6
7
  const cli_ux_1 = require("cli-ux");
@@ -341,6 +342,35 @@ const validateToken = async (token) => {
341
342
  }
342
343
  };
343
344
  exports.validateToken = validateToken;
345
+ function parseLearnpackPackageId(raw) {
346
+ if (raw === undefined || raw === null)
347
+ return null;
348
+ const n = typeof raw === "number" ? raw : Number(raw);
349
+ if (!Number.isFinite(n) || !Number.isInteger(n))
350
+ return null;
351
+ return n;
352
+ }
353
+ /**
354
+ * GET Rigobot package by slug after a successful upload. Does not throw.
355
+ * @param rigoToken Rigobot API token (Bearer-style `Token` header value).
356
+ * @param courseSlug Learnpack package slug used in the Rigobot URL path.
357
+ * @returns Resolved numeric package id from Rigobot, or `null` if the request fails, the id is missing, or it is not a finite integer.
358
+ */
359
+ async function resolveLearnpackPackageId(rigoToken, courseSlug) {
360
+ var _a;
361
+ if (!(rigoToken === null || rigoToken === void 0 ? void 0 : rigoToken.trim()) || !courseSlug)
362
+ return null;
363
+ const url = `${exports.RIGOBOT_HOST}/v1/learnpack/package/${encodeURIComponent(courseSlug)}/`;
364
+ try {
365
+ const response = await axios_1.default.get(url, {
366
+ headers: { Authorization: `Token ${rigoToken.trim()}` },
367
+ });
368
+ return parseLearnpackPackageId((_a = response.data) === null || _a === void 0 ? void 0 : _a.id);
369
+ }
370
+ catch (_b) {
371
+ return null;
372
+ }
373
+ }
344
374
  const createAsset = async (token, asset) => {
345
375
  var _a;
346
376
  const body = {
@@ -367,6 +397,7 @@ const createAsset = async (token, asset) => {
367
397
  intro_video_url: null,
368
398
  translations: [asset.lang],
369
399
  learnpack_deploy_url: asset.learnpack_deploy_url,
400
+ learnpack_id: asset.learnpack_id,
370
401
  technologies: asset.technologies,
371
402
  readme_raw: asset.readme_raw,
372
403
  all_translations: asset.all_translations,
@@ -417,9 +448,10 @@ const updateAsset = async (token, assetSlug, asset) => {
417
448
  const headers = {
418
449
  Authorization: `Token ${token}`,
419
450
  };
451
+ const body = Object.assign(Object.assign({}, asset), { learnpack_id: asset.learnpack_id });
420
452
  console.log("[BC] PUT", url, "| academy_id:", (_a = asset.academy_id) !== null && _a !== void 0 ? _a : "none");
421
453
  try {
422
- const response = await axios_1.default.put(url, asset, { headers });
454
+ const response = await axios_1.default.put(url, body, { headers });
423
455
  return response.data;
424
456
  }
425
457
  catch (error) {
@@ -551,6 +583,7 @@ exports.default = {
551
583
  createAsset: exports.createAsset,
552
584
  doesAssetExists: exports.doesAssetExists,
553
585
  updateAsset,
586
+ resolveLearnpackPackageId,
554
587
  getCategories,
555
588
  updateRigoPackage,
556
589
  createRigoPackage,
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.346",
4
+ "version": "5.0.347",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {
@@ -109,6 +109,7 @@ export const handleAssetCreation = async (
109
109
  learnpackDeployUrl: string,
110
110
  b64IndexReadme: string,
111
111
  academyId: number | undefined,
112
+ learnpackId: number,
112
113
  preflightInfo?: ExistingAssetInfo,
113
114
  all_translations: string[] = []
114
115
  ) => {
@@ -160,6 +161,7 @@ export const handleAssetCreation = async (
160
161
  preview: learnJson.preview,
161
162
  readme_raw: b64IndexReadme,
162
163
  all_translations,
164
+ learnpack_id: learnpackId,
163
165
  }
164
166
  if (academyId !== undefined) {
165
167
  assetPayload.academy_id = academyId
@@ -190,6 +192,7 @@ export const handleAssetCreation = async (
190
192
  category: category,
191
193
  description: assetDescription,
192
194
  all_translations,
195
+ learnpack_id: learnpackId,
193
196
  }
194
197
  // Only set academy when the asset has none yet and the user selected one
195
198
  if (existingAcademyId === undefined && academyId !== undefined) {
@@ -237,6 +240,17 @@ const createMultiLangAssetFromDisk = async (
237
240
  return
238
241
  }
239
242
 
243
+ const learnpackId = await api.resolveLearnpackPackageId(
244
+ sessionPayload.rigobotToken,
245
+ learnJson.slug
246
+ )
247
+ if (learnpackId === null) {
248
+ Console.warning(
249
+ "Breathecode assets skipped: could not resolve Learnpack package id"
250
+ )
251
+ return
252
+ }
253
+
240
254
  const all_translations: string[] = []
241
255
  for (const lang of availableLangs) {
242
256
  const readmePath = path.join(
@@ -266,6 +280,7 @@ const createMultiLangAssetFromDisk = async (
266
280
  deployUrl,
267
281
  b64IndexReadme,
268
282
  selectedAcademyId,
283
+ learnpackId,
269
284
  preflightInfo,
270
285
  all_translations
271
286
  )
@@ -55,6 +55,8 @@ import api, {
55
55
  RIGOBOT_REALTIME_HOST,
56
56
  listUserAcademies,
57
57
  doesAssetExists,
58
+ type AssetSyncError,
59
+ resolveLearnpackPackageId,
58
60
  } from "../utils/api"
59
61
  import {
60
62
  createUploadMiddleware,
@@ -583,6 +585,16 @@ const getLocalizedValue = (
583
585
  return typeof first === "string" ? first : ""
584
586
  }
585
587
 
588
+ function assetSyncErrorDetail(err: any): string {
589
+ if (typeof err?.detail === "string") return err.detail
590
+ if (typeof err?.message === "string") return err.message
591
+ try {
592
+ return JSON.stringify(err)
593
+ } catch {
594
+ return String(err)
595
+ }
596
+ }
597
+
586
598
  const createMultiLangAsset = async (
587
599
  bucket: Bucket,
588
600
  rigoToken: string,
@@ -591,12 +603,27 @@ const createMultiLangAsset = async (
591
603
  courseJson: any,
592
604
  deployUrl: string,
593
605
  academyId?: number
594
- ): Promise<{ errors: Array<{ lang: string; error: any }> }> => {
606
+ ): Promise<{ errors: AssetSyncError[] }> => {
607
+ const learnpackId = await resolveLearnpackPackageId(rigoToken, courseSlug)
608
+ if (learnpackId === null) {
609
+ return {
610
+ errors: [
611
+ {
612
+ kind: "package_error",
613
+ error: {
614
+ detail:
615
+ "Could not resolve Learnpack package id; assets not synced to Breathecode.",
616
+ },
617
+ },
618
+ ],
619
+ }
620
+ }
621
+
595
622
  const availableLangs = Object.keys(courseJson.title)
596
623
  console.log("AVAILABLE LANGUAGES to upload asset", availableLangs)
597
624
 
598
625
  const all_translations: string[] = []
599
- const errors: Array<{ lang: string; error: any }> = []
626
+ const errors: AssetSyncError[] = []
600
627
 
601
628
  for (const lang of availableLangs) {
602
629
  // eslint-disable-next-line no-await-in-loop
@@ -625,14 +652,16 @@ const createMultiLangAsset = async (
625
652
  deployUrl,
626
653
  b64IndexReadme,
627
654
  academyId,
655
+ learnpackId,
628
656
  undefined,
629
657
  all_translations
630
658
  )
631
659
 
632
660
  if (!asset) {
633
661
  errors.push({
662
+ kind: "lang_error",
634
663
  lang,
635
- error: { detail: "Failed to create asset", status_code: 500 },
664
+ error: { detail: "Failed to create asset" },
636
665
  })
637
666
  console.log("No se pudo crear el asset, saltando idioma", lang)
638
667
  continue
@@ -644,7 +673,11 @@ const createMultiLangAsset = async (
644
673
  error && typeof error === "object" && "response" in error ?
645
674
  (error as any).response?.data || error :
646
675
  error
647
- errors.push({ lang, error: errorData })
676
+ errors.push({
677
+ kind: "lang_error",
678
+ lang,
679
+ error: { detail: assetSyncErrorDetail(errorData) },
680
+ })
648
681
  console.error(`Error creating asset for language ${lang}:`, error)
649
682
  }
650
683
  }
@@ -5620,38 +5653,76 @@ class ServeCommand extends SessionCommand {
5620
5653
  const archive = archiver("zip", { zlib: { level: 9 } })
5621
5654
 
5622
5655
  output.on("close", async () => {
5623
- // 10) Subir ZIP a RigoBot
5624
- const form = new FormData()
5625
- form.append("file", fs.createReadStream(zipPath))
5626
- form.append("config", JSON.stringify(config))
5627
-
5628
- const rigoRes = await axios.post(
5629
- `${RIGOBOT_HOST}/v1/learnpack/upload`,
5630
- form,
5631
- {
5632
- headers: {
5633
- ...form.getHeaders(),
5634
- Authorization: "Token " + rigoToken.trim(),
5635
- },
5636
- }
5637
- )
5656
+ let rigoPublishUrl: string | undefined
5657
+ try {
5658
+ // 10) Subir ZIP a RigoBot
5659
+ const form = new FormData()
5660
+ form.append("file", fs.createReadStream(zipPath))
5661
+ form.append("config", JSON.stringify(config))
5662
+
5663
+ const rigoRes = await axios.post(
5664
+ `${RIGOBOT_HOST}/v1/learnpack/upload`,
5665
+ form,
5666
+ {
5667
+ headers: {
5668
+ ...form.getHeaders(),
5669
+ Authorization: "Token " + rigoToken.trim(),
5670
+ },
5671
+ }
5672
+ )
5673
+ rigoPublishUrl = rigoRes.data.url
5638
5674
 
5639
- const assetResults = await createMultiLangAsset(
5640
- bucket,
5641
- rigoToken,
5642
- bcToken,
5643
- slug,
5644
- fullConfig.config,
5645
- rigoRes.data.url,
5646
- academyId
5647
- )
5675
+ let errors: AssetSyncError[]
5676
+ try {
5677
+ const assetResults = await createMultiLangAsset(
5678
+ bucket,
5679
+ rigoToken,
5680
+ bcToken,
5681
+ slug,
5682
+ fullConfig.config,
5683
+ rigoRes.data.url,
5684
+ academyId
5685
+ )
5686
+ errors = assetResults.errors
5687
+ } catch (error) {
5688
+ console.error("Asset sync failed unexpectedly:", error)
5689
+ errors = [
5690
+ {
5691
+ kind: "package_error",
5692
+ error: { detail: "Asset sync failed unexpectedly." },
5693
+ },
5694
+ ]
5695
+ }
5648
5696
 
5649
- rimraf.sync(tmpRoot)
5650
- console.log("RigoRes", rigoRes.data)
5651
- return res.json({
5652
- url: rigoRes.data.url,
5653
- errors: assetResults.errors,
5654
- })
5697
+ if (res.headersSent) return
5698
+ console.log("RigoRes", rigoRes.data)
5699
+ res.json({
5700
+ url: rigoPublishUrl,
5701
+ errors,
5702
+ })
5703
+ } catch (error) {
5704
+ console.error(error)
5705
+ if (res.headersSent) return
5706
+ if (rigoPublishUrl !== undefined) {
5707
+ res.json({
5708
+ url: rigoPublishUrl,
5709
+ errors: [
5710
+ {
5711
+ kind: "package_error",
5712
+ error: { detail: "Asset sync failed unexpectedly." },
5713
+ },
5714
+ ],
5715
+ })
5716
+ } else {
5717
+ res.status(500).json({ error: (error as Error).message })
5718
+ }
5719
+ } finally {
5720
+ try {
5721
+ rimraf.sync(tmpRoot)
5722
+ } catch (error) {
5723
+ console.error("rimraf tmpRoot:", error)
5724
+ }
5725
+ }
5655
5726
  })
5656
5727
 
5657
5728
  archive.on("error", err => {