storyblok 4.17.0 → 4.17.1

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/dist/index.mjs CHANGED
@@ -1106,7 +1106,7 @@ const isVitest = process.env.VITEST === "true";
1106
1106
  const noopProgressBar = {
1107
1107
  increment: () => {
1108
1108
  },
1109
- setTotal: (_n) => {
1109
+ setTotal: () => {
1110
1110
  },
1111
1111
  stop: () => {
1112
1112
  }
@@ -1190,7 +1190,17 @@ class UI {
1190
1190
  }
1191
1191
  }
1192
1192
  createProgressBar(options) {
1193
- return this.multiBar?.create(0, 0, options) || noopProgressBar;
1193
+ const bar = this.multiBar?.create(0, 0, options);
1194
+ if (!bar) {
1195
+ return noopProgressBar;
1196
+ }
1197
+ return {
1198
+ increment: (count = 1) => bar.increment(count),
1199
+ // cli-progress renders `{eta_formatted}` as "LLs" when total is 0.
1200
+ // Floor at 1 so an empty phase stays a clean 0/1 instead.
1201
+ setTotal: (total) => bar.setTotal(Math.max(total, 1)),
1202
+ stop: () => bar.stop()
1203
+ };
1194
1204
  }
1195
1205
  stopAllProgressBars() {
1196
1206
  this.multiBar?.stop();
@@ -8968,6 +8978,105 @@ pullCmd.action(async (options, command) => {
8968
8978
  }
8969
8979
  });
8970
8980
 
8981
+ function formatStoryIdentifier(record) {
8982
+ const title = record.full_slug ?? record.slug ?? record.uuid ?? record.id?.toString() ?? record.filename ?? "unknown story";
8983
+ const extras = [];
8984
+ if (record.uuid && record.uuid !== title) {
8985
+ extras.push(`uuid: ${record.uuid}`);
8986
+ }
8987
+ if (record.filename && record.filename !== title) {
8988
+ extras.push(`file: ${record.filename}`);
8989
+ }
8990
+ return extras.length > 0 ? `${title} (${extras.join(", ")})` : title;
8991
+ }
8992
+ const ENRICHABLE_FIELDS = ["full_slug", "slug", "uuid", "id", "filename"];
8993
+ function enrichFieldMessage(detail, record) {
8994
+ const match = detail.match(/^(\w+):/);
8995
+ if (!match) {
8996
+ return detail;
8997
+ }
8998
+ const field = match[1];
8999
+ if (!ENRICHABLE_FIELDS.includes(field)) {
9000
+ return detail;
9001
+ }
9002
+ const value = record[field];
9003
+ if (value == null || value === "" || value instanceof Error) {
9004
+ return detail;
9005
+ }
9006
+ return `${field} (${String(value)})${detail.slice(field.length)}`;
9007
+ }
9008
+ function renderFailureReport(ui, records, verbose) {
9009
+ if (records.length === 0) {
9010
+ return;
9011
+ }
9012
+ ui.error(`Failed stories (${records.length}):`, void 0, { header: false, margin: false });
9013
+ const lines = [];
9014
+ for (const record of records) {
9015
+ lines.push(formatStoryIdentifier(record));
9016
+ const messages = record.error instanceof APIError ? record.error.messageStack : [record.error.message];
9017
+ const [primary, ...rest] = messages;
9018
+ lines.push(` \u2022 ${enrichFieldMessage(primary ?? "Unknown error", record)}`);
9019
+ for (const detail of rest) {
9020
+ lines.push(` \u2514\u2500 ${enrichFieldMessage(detail, record)}`);
9021
+ }
9022
+ if (verbose && record.error.stack) {
9023
+ for (const frame of record.error.stack.split("\n")) {
9024
+ lines.push(` ${frame}`);
9025
+ }
9026
+ }
9027
+ }
9028
+ ui.list(lines);
9029
+ ui.br();
9030
+ if (!verbose) {
9031
+ ui.info("Re-run with the `--verbose` flag for full stack traces.", { margin: false });
9032
+ }
9033
+ }
9034
+ class FailureCollector {
9035
+ records = /* @__PURE__ */ new Map();
9036
+ keyFor(story) {
9037
+ return story.full_slug ?? story.uuid ?? story.filename ?? `__unknown_${this.records.size}`;
9038
+ }
9039
+ /**
9040
+ * Record a failure. Returns `true` if this is the first failure seen for
9041
+ * this story's identity, `false` if one was already recorded (in which case
9042
+ * the caller should skip counter updates to avoid double-billing).
9043
+ */
9044
+ record(story, error) {
9045
+ const key = this.keyFor(story);
9046
+ if (this.records.has(key)) {
9047
+ return false;
9048
+ }
9049
+ this.records.set(key, {
9050
+ filename: story.filename,
9051
+ full_slug: story.full_slug,
9052
+ slug: story.slug,
9053
+ uuid: story.uuid,
9054
+ id: story.id,
9055
+ error
9056
+ });
9057
+ return true;
9058
+ }
9059
+ get isEmpty() {
9060
+ return this.records.size === 0;
9061
+ }
9062
+ get size() {
9063
+ return this.records.size;
9064
+ }
9065
+ render(ui, verbose = false) {
9066
+ renderFailureReport(ui, [...this.records.values()], verbose);
9067
+ }
9068
+ toReporterMeta() {
9069
+ return [...this.records.values()].map((r) => ({
9070
+ filename: r.filename,
9071
+ full_slug: r.full_slug,
9072
+ slug: r.slug,
9073
+ uuid: r.uuid,
9074
+ id: r.id,
9075
+ error: r.error.message
9076
+ }));
9077
+ }
9078
+ }
9079
+
8971
9080
  const pushCmd = storiesCommand.command("push").option("-s, --space <space>", "space ID").option("-f, --from <from>", "source space id").option("-d, --dry-run", "Preview changes without applying them to Storyblok").option("--publish", "Publish stories after pushing").option("--cleanup", "delete local stories after a successful push (note: does not cleanup manifests)").description(`Push local stories to a Storyblok space.`);
8972
9081
  pushCmd.action(async (options, command) => {
8973
9082
  const ui = getUI();
@@ -8991,6 +9100,7 @@ pushCmd.action(async (options, command) => {
8991
9100
  return;
8992
9101
  }
8993
9102
  const pendingWarnings = [];
9103
+ const failures = new FailureCollector();
8994
9104
  const summary = {
8995
9105
  creationResults: { total: 0, succeeded: 0, skipped: 0, failed: 0 },
8996
9106
  processResults: { total: 0, succeeded: 0, failed: 0 },
@@ -9064,8 +9174,10 @@ pushCmd.action(async (options, command) => {
9064
9174
  scanProgress.increment();
9065
9175
  },
9066
9176
  onError(error, filename) {
9067
- summary.creationResults.failed += 1;
9068
- handleError(error, verbose, { storyFile: filename });
9177
+ if (failures.record({ filename }, error)) {
9178
+ summary.creationResults.failed += 1;
9179
+ }
9180
+ logOnlyError(error, { storyFile: filename });
9069
9181
  }
9070
9182
  });
9071
9183
  const levels = groupStoriesByDepth(storyIndex);
@@ -9093,8 +9205,11 @@ pushCmd.action(async (options, command) => {
9093
9205
  dryRun: options.dryRun ?? false,
9094
9206
  appendToManifest,
9095
9207
  onStorySuccess(entry, remoteStory) {
9096
- if (!entry.uuid || !remoteStory.uuid) {
9097
- throw new Error("Invalid story provided!");
9208
+ if (!entry.uuid) {
9209
+ throw new Error(`Local story file "${entry.filename}" is missing a "uuid" field. Re-pull the story or add the uuid manually.`);
9210
+ }
9211
+ if (!remoteStory.uuid) {
9212
+ throw new Error(`Storyblok API returned a story without a uuid for slug "${entry.slug}".`);
9098
9213
  }
9099
9214
  maps.stories.set(entry.id, remoteStory.id);
9100
9215
  maps.stories.set(entry.uuid, remoteStory.uuid);
@@ -9103,8 +9218,11 @@ pushCmd.action(async (options, command) => {
9103
9218
  creationProgress.increment();
9104
9219
  },
9105
9220
  onStorySkipped(entry, remoteStory, reason) {
9106
- if (!entry.uuid || !remoteStory.uuid) {
9107
- throw new Error("Invalid story provided!");
9221
+ if (!entry.uuid) {
9222
+ throw new Error(`Local story file "${entry.filename}" is missing a "uuid" field. Re-pull the story or add the uuid manually.`);
9223
+ }
9224
+ if (!remoteStory.uuid) {
9225
+ throw new Error(`Storyblok API returned a story without a uuid for slug "${entry.slug}".`);
9108
9226
  }
9109
9227
  maps.stories.set(entry.id, remoteStory.id);
9110
9228
  maps.stories.set(entry.uuid, remoteStory.uuid);
@@ -9113,13 +9231,15 @@ pushCmd.action(async (options, command) => {
9113
9231
  creationProgress.increment();
9114
9232
  },
9115
9233
  onStoryError(error, entry) {
9116
- summary.creationResults.failed += 1;
9117
- summary.processResults.total -= 1;
9118
- summary.updateResults.total -= 1;
9119
- processProgress.setTotal(summary.processResults.total);
9120
- updateProgress.setTotal(summary.updateResults.total);
9234
+ if (failures.record(entry, error)) {
9235
+ summary.creationResults.failed += 1;
9236
+ summary.processResults.total -= 1;
9237
+ summary.updateResults.total -= 1;
9238
+ processProgress.setTotal(summary.processResults.total);
9239
+ updateProgress.setTotal(summary.updateResults.total);
9240
+ }
9121
9241
  creationProgress.increment();
9122
- handleError(error, verbose, { storyId: entry?.uuid });
9242
+ logOnlyError(error, { storyId: entry.uuid });
9123
9243
  }
9124
9244
  });
9125
9245
  }
@@ -9142,12 +9262,15 @@ pushCmd.action(async (options, command) => {
9142
9262
  updateProgress.setTotal(total);
9143
9263
  },
9144
9264
  onStoryError(error, filename) {
9145
- summary.creationResults.failed += 1;
9146
- summary.processResults.total -= 1;
9265
+ if (failures.record({ filename }, error)) {
9266
+ summary.processResults.failed += 1;
9267
+ } else {
9268
+ summary.processResults.total -= 1;
9269
+ processProgress.setTotal(summary.processResults.total);
9270
+ }
9147
9271
  summary.updateResults.total -= 1;
9148
- processProgress.setTotal(summary.processResults.total);
9149
9272
  updateProgress.setTotal(summary.updateResults.total);
9150
- handleError(error, verbose, { storyFile: filename });
9273
+ logOnlyError(error, { storyFile: filename });
9151
9274
  }
9152
9275
  }),
9153
9276
  // Map all references to numeric ids and uuids.
@@ -9163,10 +9286,15 @@ pushCmd.action(async (options, command) => {
9163
9286
  summary.processResults.succeeded += 1;
9164
9287
  },
9165
9288
  onStoryError(error, localStory) {
9166
- summary.processResults.failed += 1;
9289
+ logOnlyError(error, { storyId: localStory.uuid });
9290
+ if (failures.record(localStory, error)) {
9291
+ summary.processResults.failed += 1;
9292
+ } else {
9293
+ summary.processResults.total -= 1;
9294
+ processProgress.setTotal(summary.processResults.total);
9295
+ }
9167
9296
  summary.updateResults.total -= 1;
9168
9297
  updateProgress.setTotal(summary.updateResults.total);
9169
- handleError(error, verbose, { storyId: localStory.uuid });
9170
9298
  }
9171
9299
  }),
9172
9300
  // Update remote stories with correct references.
@@ -9186,8 +9314,13 @@ pushCmd.action(async (options, command) => {
9186
9314
  summary.updateResults.succeeded += 1;
9187
9315
  },
9188
9316
  onStoryError(error, localStory) {
9189
- summary.updateResults.failed += 1;
9190
- handleError(error, verbose, { storyId: localStory.uuid });
9317
+ logOnlyError(error, { storyId: localStory.uuid });
9318
+ if (failures.record(localStory, error)) {
9319
+ summary.updateResults.failed += 1;
9320
+ } else {
9321
+ summary.updateResults.total -= 1;
9322
+ updateProgress.setTotal(summary.updateResults.total);
9323
+ }
9191
9324
  }
9192
9325
  })
9193
9326
  );
@@ -9196,19 +9329,30 @@ pushCmd.action(async (options, command) => {
9196
9329
  } finally {
9197
9330
  logger.info("Pushing stories finished", summary);
9198
9331
  ui.stopAllProgressBars();
9199
- for (const warning of pendingWarnings) {
9200
- ui.warn(warning);
9201
- }
9202
- const failedStories = Math.max(summary.creationResults.failed, summary.processResults.failed, summary.updateResults.failed);
9203
- ui.info(`Push results: ${summary.creationResults.total} ${summary.creationResults.total === 1 ? "story" : "stories"} pushed, ${failedStories} ${failedStories === 1 ? "story" : "stories"} failed`);
9332
+ ui.br();
9333
+ const failedCount = failures.size;
9334
+ ui.info(`Push results: ${summary.creationResults.total} ${summary.creationResults.total === 1 ? "story" : "stories"} pushed, ${failedCount} ${failedCount === 1 ? "story" : "stories"} failed`);
9204
9335
  ui.list([
9205
9336
  `Creating stories: ${summary.creationResults.succeeded + summary.creationResults.skipped}/${summary.creationResults.total} succeeded, ${summary.creationResults.failed} failed.`,
9206
9337
  `Processing stories: ${summary.processResults.succeeded}/${summary.processResults.total} succeeded, ${summary.processResults.failed} failed.`,
9207
9338
  `Updating stories: ${summary.updateResults.succeeded}/${summary.updateResults.total} succeeded, ${summary.updateResults.failed} failed.`
9208
9339
  ]);
9340
+ if (pendingWarnings.length > 0 || !failures.isEmpty) {
9341
+ ui.br();
9342
+ }
9343
+ for (const warning of pendingWarnings) {
9344
+ ui.warn(warning);
9345
+ }
9346
+ if (pendingWarnings.length > 0 && !failures.isEmpty) {
9347
+ ui.br();
9348
+ }
9349
+ failures.render(ui, verbose);
9209
9350
  reporter.addSummary("creationResults", summary.creationResults);
9210
9351
  reporter.addSummary("processResults", summary.processResults);
9211
9352
  reporter.addSummary("updateResults", summary.updateResults);
9353
+ if (!failures.isEmpty) {
9354
+ reporter.addMeta("failedStories", failures.toReporterMeta());
9355
+ }
9212
9356
  reporter.finalize();
9213
9357
  }
9214
9358
  });