storyblok 4.16.4 → 4.16.6

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
@@ -10,9 +10,9 @@ import { readPackageUp } from 'read-package-up';
10
10
  import { Command } from 'commander';
11
11
  import { MultiBar, Presets } from 'cli-progress';
12
12
  import { Spinner } from '@topcli/spinner';
13
- import fs, { mkdir, writeFile, readFile as readFile$1, appendFile, access, constants, readdir, unlink } from 'node:fs/promises';
13
+ import fs, { mkdir, writeFile, access, constants, readFile as readFile$1, appendFile, readdir, unlink } from 'node:fs/promises';
14
14
  import filenamify from 'filenamify';
15
- import { ManagementApiClient } from '@storyblok/management-api-client';
15
+ import { ManagementApiClient, normalizeAssetUrl } from '@storyblok/management-api-client';
16
16
  import { RateLimit, Sema } from 'async-sema';
17
17
  import { select, password, input, confirm } from '@inquirer/prompts';
18
18
  import { exec, spawn } from 'node:child_process';
@@ -664,6 +664,7 @@ const API_ACTIONS = {
664
664
  update_component_internal_tag: "Failed to update component internal tag",
665
665
  update_component_group: "Failed to update component group",
666
666
  update_component_preset: "Failed to update component preset",
667
+ delete_component_preset: "Failed to delete component preset",
667
668
  pull_stories: "Failed to pull stories",
668
669
  pull_story: "Failed to pull story",
669
670
  create_story: "Failed to create story",
@@ -1357,6 +1358,15 @@ async function fileExists(path) {
1357
1358
  return false;
1358
1359
  }
1359
1360
  }
1361
+ function consolidatedFilename(defaultFilename, suffix) {
1362
+ return suffix ? `${defaultFilename}.${suffix}.json` : `${defaultFilename}.json`;
1363
+ }
1364
+ async function shouldUseSeparateFiles(resolvedPath, defaultFilename, separateFiles, suffix) {
1365
+ if (separateFiles !== void 0) {
1366
+ return separateFiles;
1367
+ }
1368
+ return !await fileExists(join(resolvedPath, consolidatedFilename(defaultFilename, suffix)));
1369
+ }
1360
1370
 
1361
1371
  const REPORT_STATUS = {
1362
1372
  unfinished: "UNFINISHED",
@@ -2502,6 +2512,20 @@ const upsertComponentPreset = async (space, preset, existingId) => {
2502
2512
  return await pushComponentPreset(space, preset);
2503
2513
  }
2504
2514
  };
2515
+ const deleteComponentPreset = async (space, presetId) => {
2516
+ try {
2517
+ const client = getMapiClient();
2518
+ await client.presets.delete({
2519
+ path: {
2520
+ space_id: Number(space),
2521
+ preset_id: presetId
2522
+ },
2523
+ throwOnError: true
2524
+ });
2525
+ } catch (error) {
2526
+ handleAPIError("delete_component_preset", error, `Failed to delete component preset ${presetId}`);
2527
+ }
2528
+ };
2505
2529
  const pushComponentInternalTag = async (space, componentInternalTag) => {
2506
2530
  try {
2507
2531
  const client = getMapiClient();
@@ -2541,7 +2565,7 @@ const upsertComponentInternalTag = async (space, tag, existingId) => {
2541
2565
  }
2542
2566
  };
2543
2567
  const readComponentsFiles = async (options) => {
2544
- const { from, path, separateFiles = false, suffix } = options;
2568
+ const { from, path, separateFiles, suffix } = options;
2545
2569
  const resolvedPath = resolvePath(path, `components/${from}`);
2546
2570
  try {
2547
2571
  await readdir(resolvedPath);
@@ -2560,7 +2584,7 @@ const readComponentsFiles = async (options) => {
2560
2584
  message
2561
2585
  );
2562
2586
  }
2563
- if (separateFiles) {
2587
+ if (await shouldUseSeparateFiles(resolvedPath, DEFAULT_COMPONENTS_FILENAME, separateFiles, suffix)) {
2564
2588
  return await readSeparateFiles$1(resolvedPath, suffix);
2565
2589
  }
2566
2590
  return await readConsolidatedFiles$1(resolvedPath, suffix);
@@ -2646,7 +2670,9 @@ async function readConsolidatedFiles$1(resolvedPath, suffix) {
2646
2670
 
2647
2671
  const pullCmd$4 = componentsCommand.command("pull [componentName]").option("-f, --filename <filename>", "custom name to be used in file(s) name instead of space id").option("--sf, --separate-files", "Argument to create a single file for each component").option("--su, --suffix <suffix>", "suffix to add to the file name (e.g. components.<suffix>.json)").option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage").description(`Download your space's components schema as json. Optionally specify a component name to pull a single component.`);
2648
2672
  pullCmd$4.action(async (componentName, options, command) => {
2649
- konsola.title(`${commands.COMPONENTS}`, colorPalette.COMPONENTS, componentName ? `Pulling component ${componentName}...` : "Pulling components...");
2673
+ const ui = getUI();
2674
+ const logger = getLogger();
2675
+ ui.title(`${commands.COMPONENTS}`, colorPalette.COMPONENTS, componentName ? `Pulling component ${componentName}...` : "Pulling components...");
2650
2676
  const { space, path, verbose } = command.optsWithGlobals();
2651
2677
  const {
2652
2678
  separateFiles = false,
@@ -2663,41 +2689,30 @@ pullCmd$4.action(async (componentName, options, command) => {
2663
2689
  handleError(new CommandError(`Please provide the space as argument --space YOUR_SPACE_ID.`), verbose);
2664
2690
  return;
2665
2691
  }
2666
- const spinnerGroups = new Spinner({
2667
- verbose: !isVitest
2668
- });
2669
- const spinnerPresets = new Spinner({
2670
- verbose: !isVitest
2671
- });
2672
- const spinnerInternalTags = new Spinner({
2673
- verbose: !isVitest
2674
- });
2675
- const spinnerComponents = new Spinner({
2676
- verbose: !isVitest
2677
- });
2692
+ logger.info("Pulling components started", { space, componentName });
2693
+ const spinnerGroups = ui.createSpinner(`Fetching ${chalk.hex(colorPalette.COMPONENTS)("components groups")}`);
2694
+ const spinnerPresets = ui.createSpinner(`Fetching ${chalk.hex(colorPalette.COMPONENTS)("components presets")}`);
2695
+ const spinnerInternalTags = ui.createSpinner(`Fetching ${chalk.hex(colorPalette.COMPONENTS)("components internal tags")}`);
2696
+ const spinnerComponents = ui.createSpinner(`Fetching ${chalk.hex(colorPalette.COMPONENTS)("components")}`);
2678
2697
  try {
2679
- spinnerGroups.start(`Fetching ${chalk.hex(colorPalette.COMPONENTS)("components groups")}`);
2680
2698
  const groups = await fetchComponentGroups(space);
2681
2699
  spinnerGroups.succeed(`${chalk.hex(colorPalette.COMPONENTS)("Groups")} - Completed in ${spinnerGroups.elapsedTime.toFixed(2)}ms`);
2682
- spinnerPresets.start(`Fetching ${chalk.hex(colorPalette.COMPONENTS)("components presets")}`);
2683
2700
  const presets = await fetchComponentPresets(space);
2684
2701
  spinnerPresets.succeed(`${chalk.hex(colorPalette.COMPONENTS)("Presets")} - Completed in ${spinnerPresets.elapsedTime.toFixed(2)}ms`);
2685
- spinnerInternalTags.start(`Fetching ${chalk.hex(colorPalette.COMPONENTS)("components internal tags")}`);
2686
2702
  const internalTags = await fetchComponentInternalTags(space);
2687
2703
  spinnerInternalTags.succeed(`${chalk.hex(colorPalette.COMPONENTS)("Tags")} - Completed in ${spinnerInternalTags.elapsedTime.toFixed(2)}ms`);
2688
2704
  let components;
2689
- spinnerComponents.start(`Fetching ${chalk.hex(colorPalette.COMPONENTS)("components")}`);
2690
2705
  if (componentName) {
2691
2706
  const component = await fetchComponent(space, componentName);
2692
2707
  if (!component) {
2693
- konsola.warn(`No component found with name "${componentName}"`);
2708
+ spinnerComponents.failed(`No component found with name "${componentName}"`);
2694
2709
  return;
2695
2710
  }
2696
2711
  components = [component];
2697
2712
  } else {
2698
2713
  components = await fetchComponents(space);
2699
2714
  if (!components || components.length === 0) {
2700
- konsola.warn(`No components found in the space ${space}`);
2715
+ spinnerComponents.failed(`No components found in the space ${space}`);
2701
2716
  return;
2702
2717
  }
2703
2718
  }
@@ -2707,33 +2722,35 @@ pullCmd$4.action(async (componentName, options, command) => {
2707
2722
  { components, groups: groups || [], presets: presets || [], internalTags: internalTags || [], datasources: [] },
2708
2723
  { ...options, path, separateFiles: separateFiles || !!componentName }
2709
2724
  );
2710
- konsola.br();
2725
+ ui.br();
2711
2726
  if (separateFiles) {
2712
2727
  if (filename && filename !== DEFAULT_COMPONENTS_FILENAME) {
2713
- konsola.warn(`The --filename option is ignored when using --separate-files`);
2728
+ ui.warn(`The --filename option is ignored when using --separate-files`);
2714
2729
  }
2715
2730
  const filePath = `${componentsOutputDir}/`;
2716
2731
  const displayPath = path && isAbsolute(path) ? filePath : `${relative(process.cwd(), componentsOutputDir)}/`;
2717
- konsola.ok(`Components downloaded successfully to ${chalk.hex(colorPalette.PRIMARY)(displayPath)}`);
2732
+ ui.ok(`Components downloaded successfully to ${chalk.hex(colorPalette.PRIMARY)(displayPath)}`);
2718
2733
  } else if (componentName) {
2719
2734
  const fileName = suffix ? `${actualFilename}.${suffix}.json` : `${componentName}.json`;
2720
2735
  const filePath = join(componentsOutputDir, fileName);
2721
2736
  const displayPath = path && isAbsolute(path) ? filePath : relative(process.cwd(), filePath);
2722
- konsola.ok(`Component ${chalk.hex(colorPalette.PRIMARY)(componentName)} downloaded successfully in ${chalk.hex(colorPalette.PRIMARY)(displayPath)}`);
2737
+ ui.ok(`Component ${chalk.hex(colorPalette.PRIMARY)(componentName)} downloaded successfully in ${chalk.hex(colorPalette.PRIMARY)(displayPath)}`);
2723
2738
  } else {
2724
2739
  const fileName = suffix ? `${actualFilename}.${suffix}.json` : `${actualFilename}.json`;
2725
2740
  const filePath = join(componentsOutputDir, fileName);
2726
2741
  const displayPath = path && isAbsolute(path) ? filePath : relative(process.cwd(), filePath);
2727
- konsola.ok(`Components downloaded successfully to ${chalk.hex(colorPalette.PRIMARY)(displayPath)}`);
2742
+ ui.ok(`Components downloaded successfully to ${chalk.hex(colorPalette.PRIMARY)(displayPath)}`);
2728
2743
  }
2729
- konsola.br();
2744
+ ui.br();
2730
2745
  } catch (error) {
2731
2746
  spinnerGroups.failed(`Pulling ${chalk.hex(colorPalette.COMPONENTS)("Groups")} - Failed`);
2732
2747
  spinnerPresets.failed(`Pulling ${chalk.hex(colorPalette.COMPONENTS)("Presets")} - Failed`);
2733
2748
  spinnerInternalTags.failed(`Pulling ${chalk.hex(colorPalette.COMPONENTS)("Tags")} - Failed`);
2734
2749
  spinnerComponents.failed(`Pulling ${chalk.hex(colorPalette.COMPONENTS)("Components")} - Failed`);
2735
- konsola.br();
2750
+ ui.br();
2736
2751
  handleError(error, verbose);
2752
+ } finally {
2753
+ logger.info("Pulling components finished", { space, componentName });
2737
2754
  }
2738
2755
  });
2739
2756
 
@@ -3682,7 +3699,9 @@ async function pushWithDependencyGraph(space, spaceState, maxConcurrency = getAc
3682
3699
 
3683
3700
  const pushCmd$3 = componentsCommand.command("push [componentName]").description(`Push your space's components schema as json`).option("-f, --from <from>", "source space id").option("--fi, --filter <filter>", "glob filter to apply to the components before pushing").option("--sf, --separate-files", "Read from separate files instead of consolidated files", false).option("--su, --suffix <suffix>", "Suffix to add to the component name").option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage");
3684
3701
  pushCmd$3.action(async (componentName, options, command) => {
3685
- konsola.title(`${commands.COMPONENTS}`, colorPalette.COMPONENTS, componentName ? `Pushing component ${componentName}...` : "Pushing components...");
3702
+ const ui = getUI();
3703
+ const logger = getLogger();
3704
+ ui.title(`${commands.COMPONENTS}`, colorPalette.COMPONENTS, componentName ? `Pushing component ${componentName}...` : "Pushing components...");
3686
3705
  const { space, path, verbose } = command.optsWithGlobals();
3687
3706
  const { filter } = options;
3688
3707
  const fromSpace = options.from || space;
@@ -3694,8 +3713,9 @@ pushCmd$3.action(async (componentName, options, command) => {
3694
3713
  handleError(new CommandError(`Please provide the target space as argument --space TARGET_SPACE_ID.`), verbose);
3695
3714
  return;
3696
3715
  }
3697
- konsola.info(`Attempting to push components ${chalk.bold("from")} space ${chalk.hex(colorPalette.COMPONENTS)(fromSpace)} ${chalk.bold("to")} ${chalk.hex(colorPalette.COMPONENTS)(space)}`);
3698
- konsola.br();
3716
+ logger.info("Pushing components started", { space, fromSpace, componentName });
3717
+ ui.info(`Attempting to push components ${chalk.bold("from")} space ${chalk.hex(colorPalette.COMPONENTS)(fromSpace)} ${chalk.bold("to")} ${chalk.hex(colorPalette.COMPONENTS)(space)}`);
3718
+ ui.br();
3699
3719
  let requestCount = 0;
3700
3720
  const client = getMapiClient();
3701
3721
  client.interceptors.request.use((config) => {
@@ -3765,24 +3785,45 @@ pushCmd$3.action(async (componentName, options, command) => {
3765
3785
  handleError(new CommandError(`No components found matching pattern "${filter}".`), verbose);
3766
3786
  return;
3767
3787
  }
3768
- konsola.info(`Filter applied: ${filter}`);
3788
+ ui.info(`Filter applied: ${filter}`);
3769
3789
  }
3770
3790
  if (!spaceState.local.components.length) {
3771
- konsola.warn("No components found. Please make sure you have pulled the components first.");
3791
+ ui.warn("No components found. Please make sure you have pulled the components first.");
3772
3792
  return;
3773
3793
  }
3774
3794
  const results = {
3775
3795
  successful: [],
3776
3796
  failed: []
3777
3797
  };
3778
- konsola.info("Using graph-based dependency resolution");
3798
+ const localComponentById = new Map(spaceState.local.components.map((c) => [c.id, c.name]));
3799
+ const localPresetKeys = /* @__PURE__ */ new Set();
3800
+ for (const preset of spaceState.local.presets) {
3801
+ const componentName2 = localComponentById.get(preset.component_id);
3802
+ if (componentName2) {
3803
+ localPresetKeys.add(`${componentName2}:${preset.name}`);
3804
+ }
3805
+ }
3806
+ ui.info("Using graph-based dependency resolution");
3779
3807
  const graphResults = await pushWithDependencyGraph(space, spaceState);
3780
3808
  results.successful.push(...graphResults.successful);
3781
3809
  results.failed.push(...graphResults.failed);
3810
+ const successfulNames = new Set(results.successful);
3811
+ for (const [compositeKey, targetPreset] of spaceState.target.presets) {
3812
+ const separatorIndex = compositeKey.indexOf(":");
3813
+ const componentName2 = compositeKey.substring(0, separatorIndex);
3814
+ if (successfulNames.has(componentName2) && !localPresetKeys.has(compositeKey)) {
3815
+ try {
3816
+ await deleteComponentPreset(space, targetPreset.id);
3817
+ ui.info(`Deleted stale preset: ${chalk.hex(colorPalette.PRESETS)(compositeKey)}`);
3818
+ } catch (error) {
3819
+ results.failed.push({ name: compositeKey, error });
3820
+ }
3821
+ }
3822
+ }
3782
3823
  if (results.failed.length > 0) {
3783
3824
  if (!verbose) {
3784
- konsola.br();
3785
- konsola.info("For more information about the error, run the command with the `--verbose` flag");
3825
+ ui.br();
3826
+ ui.info("For more information about the error, run the command with the `--verbose` flag");
3786
3827
  } else {
3787
3828
  results.failed.forEach((failed) => {
3788
3829
  handleError(failed.error, verbose);
@@ -3806,12 +3847,14 @@ pushCmd$3.action(async (componentName, options, command) => {
3806
3847
  }
3807
3848
  });
3808
3849
  if (referencedDatasources.size > 0) {
3809
- konsola.br();
3810
- konsola.info(`Components reference datasources: ${chalk.yellow(Array.from(referencedDatasources).join(", "))}`);
3811
- konsola.info(`To manage datasources, use: ${chalk.cyan("storyblok datasources push")}`);
3850
+ ui.br();
3851
+ ui.info(`Components reference datasources: ${chalk.yellow(Array.from(referencedDatasources).join(", "))}`);
3852
+ ui.info(`To manage datasources, use: ${chalk.cyan("storyblok datasources push")}`);
3812
3853
  }
3813
3854
  } catch (error) {
3814
3855
  handleError(error, verbose);
3856
+ } finally {
3857
+ logger.info("Pushing components finished", { space, fromSpace, componentName });
3815
3858
  }
3816
3859
  });
3817
3860
 
@@ -5947,7 +5990,7 @@ const deleteDatasourceEntry = async (spaceId, entryId) => {
5947
5990
  }
5948
5991
  };
5949
5992
  const readDatasourcesFiles = async (options) => {
5950
- const { from, path, separateFiles = false, suffix } = options;
5993
+ const { from, path, separateFiles, suffix } = options;
5951
5994
  const resolvedPath = resolvePath(path, `datasources/${from}`);
5952
5995
  try {
5953
5996
  await readdir(resolvedPath);
@@ -5966,7 +6009,7 @@ const readDatasourcesFiles = async (options) => {
5966
6009
  message
5967
6010
  );
5968
6011
  }
5969
- if (separateFiles) {
6012
+ if (await shouldUseSeparateFiles(resolvedPath, DEFAULT_DATASOURCES_FILENAME, separateFiles, suffix)) {
5970
6013
  return await readSeparateFiles(resolvedPath, suffix);
5971
6014
  }
5972
6015
  return await readConsolidatedFiles(resolvedPath, suffix);
@@ -6030,7 +6073,6 @@ generateCmd.action(async (options, command) => {
6030
6073
  const componentsData = await readComponentsFiles({
6031
6074
  from: space,
6032
6075
  path,
6033
- separateFiles,
6034
6076
  suffix,
6035
6077
  verbose
6036
6078
  });
@@ -6039,7 +6081,6 @@ generateCmd.action(async (options, command) => {
6039
6081
  dataSourceData = await readDatasourcesFiles({
6040
6082
  from: space,
6041
6083
  path,
6042
- separateFiles,
6043
6084
  suffix,
6044
6085
  verbose
6045
6086
  });
@@ -8074,9 +8115,13 @@ const bloksFieldRefMapper = (data, { schemas, maps, fieldRefMappers: fieldRefMap
8074
8115
  };
8075
8116
  const assetFieldRefMapper = (data, { maps }) => {
8076
8117
  const mappedAsset = typeof data.id === "number" ? maps.assets?.get(data.id) : void 0;
8118
+ if (!mappedAsset) {
8119
+ return data;
8120
+ }
8077
8121
  return {
8078
8122
  ...data,
8079
- ...mappedAsset?.new
8123
+ ...mappedAsset.new,
8124
+ filename: normalizeAssetUrl(mappedAsset.new.filename)
8080
8125
  };
8081
8126
  };
8082
8127
  const multiassetFieldRefMapper = (data, options) => {