storyblok 4.3.1 → 4.3.2

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
@@ -9,6 +9,7 @@ import { Spinner } from '@topcli/spinner';
9
9
  import { select, password, input, confirm } from '@inquirer/prompts';
10
10
  import fs, { mkdir, writeFile, readFile as readFile$1, access, readdir } from 'node:fs/promises';
11
11
  import path, { join, parse, resolve } from 'node:path';
12
+ import filenamify from 'filenamify';
12
13
  import { exec, spawn } from 'node:child_process';
13
14
  import { promisify } from 'node:util';
14
15
  import { getRegion } from '@storyblok/region-helper';
@@ -614,6 +615,11 @@ const resolvePath = (path, folder) => {
614
615
  const getComponentNameFromFilename = (filename) => {
615
616
  return filename.replace(/\.js$/, "");
616
617
  };
618
+ const sanitizeFilename = (filename) => {
619
+ return filenamify(filename, {
620
+ replacement: "_"
621
+ });
622
+ };
617
623
  async function readJsonFile(filePath) {
618
624
  try {
619
625
  const content = (await readFile(filePath)).toString();
@@ -1246,11 +1252,12 @@ const saveComponentsToFiles = async (space, spaceData, options) => {
1246
1252
  try {
1247
1253
  if (separateFiles) {
1248
1254
  for (const component of components) {
1249
- const componentFilePath = join(resolvedPath, suffix ? `${component.name}.${suffix}.json` : `${component.name}.json`);
1255
+ const sanitizedName = sanitizeFilename(component.name);
1256
+ const componentFilePath = join(resolvedPath, suffix ? `${sanitizedName}.${suffix}.json` : `${sanitizedName}.json`);
1250
1257
  await saveToFile(componentFilePath, JSON.stringify(component, null, 2));
1251
1258
  const componentPresets = presets.filter((preset) => preset.component_id === component.id);
1252
1259
  if (componentPresets.length > 0) {
1253
- const presetsFilePath = join(resolvedPath, suffix ? `${component.name}.presets.${suffix}.json` : `${component.name}.presets.json`);
1260
+ const presetsFilePath = join(resolvedPath, suffix ? `${sanitizedName}.presets.${suffix}.json` : `${sanitizedName}.presets.json`);
1254
1261
  await saveToFile(presetsFilePath, JSON.stringify(componentPresets, null, 2));
1255
1262
  }
1256
1263
  const groupsFilePath = join(resolvedPath, suffix ? `groups.${suffix}.json` : `groups.json`);
@@ -1592,153 +1599,6 @@ componentsCommand.command("pull [componentName]").option("-f, --filename <filena
1592
1599
  }
1593
1600
  });
1594
1601
 
1595
- const pushDatasource = async (space, datasource) => {
1596
- try {
1597
- const client = mapiClient();
1598
- const { data } = await client.post(`spaces/${space}/datasources`, {
1599
- body: JSON.stringify(datasource)
1600
- });
1601
- return data.datasource;
1602
- } catch (error) {
1603
- handleAPIError("push_datasource", error, `Failed to push datasource ${datasource.name}`);
1604
- }
1605
- };
1606
- const updateDatasource = async (space, datasourceId, datasource) => {
1607
- try {
1608
- const client = mapiClient();
1609
- const { data } = await client.put(`spaces/${space}/datasources/${datasourceId}`, {
1610
- body: JSON.stringify(datasource)
1611
- });
1612
- return data.datasource;
1613
- } catch (error) {
1614
- handleAPIError("update_datasource", error, `Failed to update datasource ${datasource.name}`);
1615
- }
1616
- };
1617
- const upsertDatasource = async (space, datasource, existingId) => {
1618
- if (existingId) {
1619
- return await updateDatasource(space, existingId, datasource);
1620
- } else {
1621
- return await pushDatasource(space, datasource);
1622
- }
1623
- };
1624
- const pushDatasourceEntry = async (space, datasourceId, entry) => {
1625
- try {
1626
- const client = mapiClient();
1627
- const { data } = await client.post(`spaces/${space}/datasource_entries`, {
1628
- body: JSON.stringify({
1629
- datasource_entry: {
1630
- ...entry,
1631
- datasource_id: datasourceId
1632
- }
1633
- })
1634
- });
1635
- return data.datasource_entry;
1636
- } catch (error) {
1637
- handleAPIError("push_datasource", error, `Failed to push datasource entry ${entry.name}`);
1638
- }
1639
- };
1640
- const updateDatasourceEntry = async (space, entryId, entry) => {
1641
- try {
1642
- const client = mapiClient();
1643
- await client.put(`spaces/${space}/datasource_entries/${entryId}`, {
1644
- body: JSON.stringify({
1645
- datasource_entry: entry
1646
- })
1647
- });
1648
- } catch (error) {
1649
- handleAPIError("update_datasource", error, `Failed to update datasource entry ${entry.name}`);
1650
- }
1651
- };
1652
- const upsertDatasourceEntry = async (space, datasourceId, entry, existingId) => {
1653
- if (existingId) {
1654
- await updateDatasourceEntry(space, existingId, entry);
1655
- return void 0;
1656
- } else {
1657
- return await pushDatasourceEntry(space, datasourceId, entry);
1658
- }
1659
- };
1660
- const readDatasourcesFiles = async (options) => {
1661
- const { from, path, separateFiles = false, suffix, space } = options;
1662
- const resolvedPath = resolvePath(path, `datasources/${from}`);
1663
- try {
1664
- await readdir(resolvedPath);
1665
- } catch (error) {
1666
- const message = `No local datasources found for space ${chalk.bold(from)}. To push datasources, you need to pull them first:
1667
-
1668
- 1. Pull the datasources from your source space:
1669
- ${chalk.cyan(`storyblok datasources pull --space ${from}`)}
1670
-
1671
- 2. Then try pushing again:
1672
- ${chalk.cyan(`storyblok datasources push --space ${space} --from ${from}`)}`;
1673
- throw new FileSystemError(
1674
- "file_not_found",
1675
- "read",
1676
- error,
1677
- message
1678
- );
1679
- }
1680
- if (separateFiles) {
1681
- return await readSeparateFiles(resolvedPath, suffix);
1682
- }
1683
- return await readConsolidatedFiles(resolvedPath, suffix);
1684
- };
1685
- async function readSeparateFiles(resolvedPath, suffix) {
1686
- const files = await readdir(resolvedPath);
1687
- const datasources = [];
1688
- const filteredFiles = files.filter((file) => {
1689
- if (suffix) {
1690
- return file.endsWith(`.${suffix}.json`);
1691
- } else {
1692
- return !/\.\w+\.json$/.test(file);
1693
- }
1694
- });
1695
- for (const file of filteredFiles) {
1696
- const filePath = join(resolvedPath, file);
1697
- if (file.endsWith(".json") || file.endsWith(`${suffix}.json`)) {
1698
- if (file === "datasources.json" || /^datasources\.\w+\.json$/.test(file)) {
1699
- continue;
1700
- }
1701
- const result = await readJsonFile(filePath);
1702
- if (result.error) {
1703
- handleFileSystemError("read", result.error);
1704
- continue;
1705
- }
1706
- datasources.push(...result.data);
1707
- }
1708
- }
1709
- return {
1710
- datasources
1711
- };
1712
- }
1713
- async function readConsolidatedFiles(resolvedPath, suffix) {
1714
- const datasourcesPath = join(resolvedPath, suffix ? `datasources.${suffix}.json` : "datasources.json");
1715
- const datasourcesResult = await readJsonFile(datasourcesPath);
1716
- if (datasourcesResult.error || !datasourcesResult.data.length) {
1717
- throw new FileSystemError(
1718
- "file_not_found",
1719
- "read",
1720
- datasourcesResult.error || new Error("Datasources file is empty"),
1721
- `No datasources found in ${datasourcesPath}. Please make sure you have pulled the datasources first.`
1722
- );
1723
- }
1724
- return {
1725
- datasources: datasourcesResult.data
1726
- };
1727
- }
1728
-
1729
- function createStubDatasource(name) {
1730
- return {
1731
- id: 0,
1732
- // Will be set by API
1733
- name,
1734
- slug: name,
1735
- dimensions: [],
1736
- entries: [],
1737
- // Empty entries for stub
1738
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
1739
- updated_at: (/* @__PURE__ */ new Date()).toISOString()
1740
- };
1741
- }
1742
1602
  function buildDependencyGraph(context) {
1743
1603
  const { spaceState } = context;
1744
1604
  const graph = { nodes: /* @__PURE__ */ new Map() };
@@ -1750,22 +1610,6 @@ function buildDependencyGraph(context) {
1750
1610
  dependency.dependents.add(dependentId);
1751
1611
  }
1752
1612
  }
1753
- const referencedDatasources = /* @__PURE__ */ new Set();
1754
- spaceState.local.components.forEach((component) => {
1755
- if (component.schema) {
1756
- const dependencies = collectWhitelistDependencies(component.schema);
1757
- dependencies.datasourceNames.forEach((datasourceName) => {
1758
- referencedDatasources.add(datasourceName);
1759
- });
1760
- }
1761
- });
1762
- referencedDatasources.forEach((datasourceName) => {
1763
- const nodeId = `datasource:${datasourceName}`;
1764
- const targetDatasource = spaceState.target.datasources?.get(datasourceName);
1765
- const stubDatasource = createStubDatasource(datasourceName);
1766
- const node = new DatasourceNode(nodeId, stubDatasource, targetDatasource);
1767
- graph.nodes.set(nodeId, node);
1768
- });
1769
1613
  spaceState.local.internalTags.forEach((tag) => {
1770
1614
  const nodeId = `tag:${tag.id}`;
1771
1615
  const targetTag = spaceState.target.tags.get(tag.name);
@@ -1841,10 +1685,6 @@ function buildDependencyGraph(context) {
1841
1685
  const dependencyId = `component:${componentName}`;
1842
1686
  addDependency(componentId, dependencyId);
1843
1687
  });
1844
- dependencies.datasourceNames.forEach((datasourceName) => {
1845
- const datasourceId = `datasource:${datasourceName}`;
1846
- addDependency(componentId, datasourceId);
1847
- });
1848
1688
  }
1849
1689
  });
1850
1690
  spaceState.local.presets.forEach((preset) => {
@@ -2227,15 +2067,6 @@ class ComponentNode extends GraphNode {
2227
2067
  });
2228
2068
  }
2229
2069
  }
2230
- if ((resolvedField.type === "option" || resolvedField.type === "options") && resolvedField.source === "internal") {
2231
- if (resolvedField.datasource_slug && typeof resolvedField.datasource_slug === "string") {
2232
- const datasourceNodeId = `datasource:${resolvedField.datasource_slug}`;
2233
- const datasourceNode = graph.nodes.get(datasourceNodeId);
2234
- if (datasourceNode?.targetData) {
2235
- resolvedField.datasource_slug = datasourceNode.targetData.resource.slug;
2236
- }
2237
- }
2238
- }
2239
2070
  Object.keys(resolvedField).forEach((key) => {
2240
2071
  if (typeof resolvedField[key] === "object" && resolvedField[key] !== null) {
2241
2072
  resolvedField[key] = resolveField(resolvedField[key]);
@@ -2307,23 +2138,6 @@ class PresetNode {
2307
2138
  };
2308
2139
  }
2309
2140
  }
2310
- class DatasourceNode extends GraphNode {
2311
- constructor(id, data, targetDatasource) {
2312
- super(id, "datasource", data.name, data, targetDatasource);
2313
- }
2314
- resolveReferences(_graph) {
2315
- }
2316
- async upsert(space) {
2317
- const existingDatasource = this.targetData?.resource;
2318
- const existingId = existingDatasource?.id;
2319
- const { entries, ...datasourceDefinition } = this.sourceData;
2320
- const result = await upsertDatasource(space, datasourceDefinition, existingId);
2321
- if (!result) {
2322
- throw new Error(`Failed to upsert datasource ${this.name}`);
2323
- }
2324
- return result;
2325
- }
2326
- }
2327
2141
 
2328
2142
  function collectAllDependencies(components, allComponents, allGroups, allTags) {
2329
2143
  const requiredComponents = /* @__PURE__ */ new Set();
@@ -2724,63 +2538,6 @@ async function pushWithDependencyGraph(space, spaceState, maxConcurrency = 5) {
2724
2538
  return results;
2725
2539
  }
2726
2540
 
2727
- const fetchDatasourceEntries = async (space, datasourceId) => {
2728
- try {
2729
- const client = mapiClient();
2730
- const { data } = await client.get(`spaces/${space}/datasource_entries?datasource_id=${datasourceId}`);
2731
- return data.datasource_entries;
2732
- } catch (error) {
2733
- handleAPIError("pull_datasources", error);
2734
- }
2735
- };
2736
- const fetchDatasources = async (space) => {
2737
- try {
2738
- const client = mapiClient();
2739
- const { data } = await client.get(`spaces/${space}/datasources`);
2740
- const datasources = data.datasources;
2741
- const datasourcesWithEntries = await Promise.all(
2742
- datasources.map(async (ds) => {
2743
- const entries = await fetchDatasourceEntries(space, ds.id);
2744
- return { ...ds, entries };
2745
- })
2746
- );
2747
- return datasourcesWithEntries;
2748
- } catch (error) {
2749
- handleAPIError("pull_datasources", error);
2750
- }
2751
- };
2752
- const fetchDatasource = async (space, datasourceName) => {
2753
- try {
2754
- const client = mapiClient();
2755
- const { data } = await client.get(`spaces/${space}/datasources?search=${encodeURIComponent(datasourceName)}`);
2756
- const found = data.datasources?.find((d) => d.name === datasourceName);
2757
- if (!found) {
2758
- return void 0;
2759
- }
2760
- const entries = await fetchDatasourceEntries(space, found.id);
2761
- return { ...found, entries };
2762
- } catch (error) {
2763
- handleAPIError("pull_datasources", error, `Failed to fetch datasource ${datasourceName}`);
2764
- }
2765
- };
2766
- const saveDatasourcesToFiles = async (space, datasources, options) => {
2767
- const { filename = "datasources", suffix, path, separateFiles } = options;
2768
- const resolvedPath = path ? resolve(process.cwd(), path, "datasources", space) : resolvePath(path, `datasources/${space}`);
2769
- try {
2770
- if (separateFiles) {
2771
- for (const datasource of datasources) {
2772
- const datasourceFilePath = join(resolvedPath, suffix ? `${datasource.name}.${suffix}.json` : `${datasource.name}.json`);
2773
- await saveToFile(datasourceFilePath, JSON.stringify(datasource, null, 2));
2774
- }
2775
- return;
2776
- }
2777
- const datasourcesFilePath = join(resolvedPath, suffix ? `${filename}.${suffix}.json` : `${filename}.json`);
2778
- await saveToFile(datasourcesFilePath, JSON.stringify(datasources, null, 2));
2779
- } catch (error) {
2780
- handleFileSystemError("write", error);
2781
- }
2782
- };
2783
-
2784
2541
  const program$c = getProgram();
2785
2542
  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").option("--su, --suffix <suffix>", "Suffix to add to the component name").action(async (componentName, options) => {
2786
2543
  konsola.title(`${commands.COMPONENTS}`, colorPalette.COMPONENTS, componentName ? `Pushing component ${componentName}...` : "Pushing components...");
@@ -2834,10 +2591,9 @@ componentsCommand.command("push [componentName]").description(`Push your space's
2834
2591
  fetchComponents(space),
2835
2592
  fetchComponentGroups(space),
2836
2593
  fetchComponentPresets(space),
2837
- fetchComponentInternalTags(space),
2838
- fetchDatasources(space)
2594
+ fetchComponentInternalTags(space)
2839
2595
  ];
2840
- const [components, groups, presets, internalTags, datasources] = await Promise.all(promises);
2596
+ const [components, groups, presets, internalTags] = await Promise.all(promises);
2841
2597
  if (components) {
2842
2598
  components.forEach((component) => {
2843
2599
  spaceState.target.components.set(component.name, component);
@@ -2862,11 +2618,6 @@ componentsCommand.command("push [componentName]").description(`Push your space's
2862
2618
  spaceState.target.tags.set(tag.name, tag);
2863
2619
  });
2864
2620
  }
2865
- if (datasources) {
2866
- datasources.forEach((datasource) => {
2867
- spaceState.target.datasources.set(datasource.name, datasource);
2868
- });
2869
- }
2870
2621
  if (componentName) {
2871
2622
  spaceState.local = filterSpaceDataByComponent(spaceState.local, componentName);
2872
2623
  if (!spaceState.local.components.length) {
@@ -2904,6 +2655,26 @@ componentsCommand.command("push [componentName]").description(`Push your space's
2904
2655
  }
2905
2656
  }
2906
2657
  console.log(`${requestCount} requests made`);
2658
+ const referencedDatasources = /* @__PURE__ */ new Set();
2659
+ spaceState.local.components.forEach((component) => {
2660
+ if (component.schema) {
2661
+ const fields = JSON.stringify(component.schema);
2662
+ const datasourceMatches = fields.match(/"datasource_slug"\s*:\s*"([^"]+)"/g);
2663
+ if (datasourceMatches) {
2664
+ datasourceMatches.forEach((match) => {
2665
+ const slug = match.match(/"([^"]+)"$/)?.[1];
2666
+ if (slug) {
2667
+ referencedDatasources.add(slug);
2668
+ }
2669
+ });
2670
+ }
2671
+ }
2672
+ });
2673
+ if (referencedDatasources.size > 0) {
2674
+ konsola.br();
2675
+ konsola.info(`Components reference datasources: ${chalk.yellow(Array.from(referencedDatasources).join(", "))}`);
2676
+ konsola.info(`To manage datasources, use: ${chalk.cyan("storyblok datasources push")}`);
2677
+ }
2907
2678
  } catch (error) {
2908
2679
  handleError(error, verbose);
2909
2680
  }
@@ -4176,6 +3947,15 @@ const getComponentPropertiesTypeAnnotations = async (component, options, spaceDa
4176
3947
  );
4177
3948
  propertyTypeAnnotation[key].tsType = componentsInGroupWhitelist.length > 0 ? `(${componentsInGroupWhitelist.join(" | ")})[]` : `never[]`;
4178
3949
  }
3950
+ } else if (value.restrict_type === "tags") {
3951
+ if (Array.isArray(value.component_tag_whitelist) && value.component_tag_whitelist.length > 0) {
3952
+ const componentsWithTags = spaceData.components.filter(
3953
+ (component2) => component2.internal_tag_ids && component2.internal_tag_ids.some(
3954
+ (tagId) => value.component_tag_whitelist.includes(Number(tagId))
3955
+ )
3956
+ );
3957
+ propertyTypeAnnotation[key].tsType = componentsWithTags.length > 0 ? `(${componentsWithTags.map((component2) => getComponentType(component2.name, options)).join(" | ")})[]` : `never[]`;
3958
+ }
4179
3959
  } else {
4180
3960
  if (Array.isArray(value.component_whitelist) && value.component_whitelist.length > 0) {
4181
3961
  propertyTypeAnnotation[key].tsType = `(${value.component_whitelist.map((name) => getComponentType(name, options)).join(" | ")})[]`;
@@ -4366,6 +4146,64 @@ typesCommand.command("generate").description("Generate types d.ts for your compo
4366
4146
  const program$4 = getProgram();
4367
4147
  const datasourcesCommand = program$4.command(commands.DATASOURCES).alias("ds").description(`Manage your space's datasources`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/datasources").hook("preAction", resolveRegion);
4368
4148
 
4149
+ const fetchDatasourceEntries = async (space, datasourceId) => {
4150
+ try {
4151
+ const client = mapiClient();
4152
+ const { data } = await client.get(`spaces/${space}/datasource_entries?datasource_id=${datasourceId}`);
4153
+ return data.datasource_entries;
4154
+ } catch (error) {
4155
+ handleAPIError("pull_datasources", error);
4156
+ }
4157
+ };
4158
+ const fetchDatasources = async (space) => {
4159
+ try {
4160
+ const client = mapiClient();
4161
+ const { data } = await client.get(`spaces/${space}/datasources`);
4162
+ const datasources = data.datasources;
4163
+ const datasourcesWithEntries = await Promise.all(
4164
+ datasources.map(async (ds) => {
4165
+ const entries = await fetchDatasourceEntries(space, ds.id);
4166
+ return { ...ds, entries };
4167
+ })
4168
+ );
4169
+ return datasourcesWithEntries;
4170
+ } catch (error) {
4171
+ handleAPIError("pull_datasources", error);
4172
+ }
4173
+ };
4174
+ const fetchDatasource = async (space, datasourceName) => {
4175
+ try {
4176
+ const client = mapiClient();
4177
+ const { data } = await client.get(`spaces/${space}/datasources?search=${encodeURIComponent(datasourceName)}`);
4178
+ const found = data.datasources?.find((d) => d.name === datasourceName);
4179
+ if (!found) {
4180
+ return void 0;
4181
+ }
4182
+ const entries = await fetchDatasourceEntries(space, found.id);
4183
+ return { ...found, entries };
4184
+ } catch (error) {
4185
+ handleAPIError("pull_datasources", error, `Failed to fetch datasource ${datasourceName}`);
4186
+ }
4187
+ };
4188
+ const saveDatasourcesToFiles = async (space, datasources, options) => {
4189
+ const { filename = "datasources", suffix, path, separateFiles } = options;
4190
+ const resolvedPath = path ? resolve(process.cwd(), path, "datasources", space) : resolvePath(path, `datasources/${space}`);
4191
+ try {
4192
+ if (separateFiles) {
4193
+ for (const datasource of datasources) {
4194
+ const sanitizedName = sanitizeFilename(datasource.name);
4195
+ const datasourceFilePath = join(resolvedPath, suffix ? `${sanitizedName}.${suffix}.json` : `${sanitizedName}.json`);
4196
+ await saveToFile(datasourceFilePath, JSON.stringify(datasource, null, 2));
4197
+ }
4198
+ return;
4199
+ }
4200
+ const datasourcesFilePath = join(resolvedPath, suffix ? `${filename}.${suffix}.json` : `${filename}.json`);
4201
+ await saveToFile(datasourcesFilePath, JSON.stringify(datasources, null, 2));
4202
+ } catch (error) {
4203
+ handleFileSystemError("write", error);
4204
+ }
4205
+ };
4206
+
4369
4207
  const program$3 = getProgram();
4370
4208
  datasourcesCommand.command("pull [datasourceName]").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 datasource").option("--su, --suffix <suffix>", "suffix to add to the file name (e.g. datasources.<suffix>.json)").description("Pull datasources from your space").action(async (datasourceName, options) => {
4371
4209
  konsola.title(`${commands.DATASOURCES}`, colorPalette.DATASOURCES, datasourceName ? `Pulling datasource ${datasourceName}...` : "Pulling datasources...");
@@ -4436,6 +4274,140 @@ datasourcesCommand.command("pull [datasourceName]").option("-f, --filename <file
4436
4274
  }
4437
4275
  });
4438
4276
 
4277
+ const pushDatasource = async (space, datasource) => {
4278
+ try {
4279
+ const client = mapiClient();
4280
+ const { data } = await client.post(`spaces/${space}/datasources`, {
4281
+ body: JSON.stringify(datasource)
4282
+ });
4283
+ return data.datasource;
4284
+ } catch (error) {
4285
+ handleAPIError("push_datasource", error, `Failed to push datasource ${datasource.name}`);
4286
+ }
4287
+ };
4288
+ const updateDatasource = async (space, datasourceId, datasource) => {
4289
+ try {
4290
+ const client = mapiClient();
4291
+ const { data } = await client.put(`spaces/${space}/datasources/${datasourceId}`, {
4292
+ body: JSON.stringify(datasource)
4293
+ });
4294
+ return data.datasource;
4295
+ } catch (error) {
4296
+ handleAPIError("update_datasource", error, `Failed to update datasource ${datasource.name}`);
4297
+ }
4298
+ };
4299
+ const upsertDatasource = async (space, datasource, existingId) => {
4300
+ if (existingId) {
4301
+ return await updateDatasource(space, existingId, datasource);
4302
+ } else {
4303
+ return await pushDatasource(space, datasource);
4304
+ }
4305
+ };
4306
+ const pushDatasourceEntry = async (space, datasourceId, entry) => {
4307
+ try {
4308
+ const client = mapiClient();
4309
+ const { data } = await client.post(`spaces/${space}/datasource_entries`, {
4310
+ body: JSON.stringify({
4311
+ datasource_entry: {
4312
+ ...entry,
4313
+ datasource_id: datasourceId
4314
+ }
4315
+ })
4316
+ });
4317
+ return data.datasource_entry;
4318
+ } catch (error) {
4319
+ handleAPIError("push_datasource", error, `Failed to push datasource entry ${entry.name}`);
4320
+ }
4321
+ };
4322
+ const updateDatasourceEntry = async (space, entryId, entry) => {
4323
+ try {
4324
+ const client = mapiClient();
4325
+ await client.put(`spaces/${space}/datasource_entries/${entryId}`, {
4326
+ body: JSON.stringify({
4327
+ datasource_entry: entry
4328
+ })
4329
+ });
4330
+ } catch (error) {
4331
+ handleAPIError("update_datasource", error, `Failed to update datasource entry ${entry.name}`);
4332
+ }
4333
+ };
4334
+ const upsertDatasourceEntry = async (space, datasourceId, entry, existingId) => {
4335
+ if (existingId) {
4336
+ await updateDatasourceEntry(space, existingId, entry);
4337
+ return void 0;
4338
+ } else {
4339
+ return await pushDatasourceEntry(space, datasourceId, entry);
4340
+ }
4341
+ };
4342
+ const readDatasourcesFiles = async (options) => {
4343
+ const { from, path, separateFiles = false, suffix, space } = options;
4344
+ const resolvedPath = resolvePath(path, `datasources/${from}`);
4345
+ try {
4346
+ await readdir(resolvedPath);
4347
+ } catch (error) {
4348
+ const message = `No local datasources found for space ${chalk.bold(from)}. To push datasources, you need to pull them first:
4349
+
4350
+ 1. Pull the datasources from your source space:
4351
+ ${chalk.cyan(`storyblok datasources pull --space ${from}`)}
4352
+
4353
+ 2. Then try pushing again:
4354
+ ${chalk.cyan(`storyblok datasources push --space ${space} --from ${from}`)}`;
4355
+ throw new FileSystemError(
4356
+ "file_not_found",
4357
+ "read",
4358
+ error,
4359
+ message
4360
+ );
4361
+ }
4362
+ if (separateFiles) {
4363
+ return await readSeparateFiles(resolvedPath, suffix);
4364
+ }
4365
+ return await readConsolidatedFiles(resolvedPath, suffix);
4366
+ };
4367
+ async function readSeparateFiles(resolvedPath, suffix) {
4368
+ const files = await readdir(resolvedPath);
4369
+ const datasources = [];
4370
+ const filteredFiles = files.filter((file) => {
4371
+ if (suffix) {
4372
+ return file.endsWith(`.${suffix}.json`);
4373
+ } else {
4374
+ return !/\.\w+\.json$/.test(file);
4375
+ }
4376
+ });
4377
+ for (const file of filteredFiles) {
4378
+ const filePath = join(resolvedPath, file);
4379
+ if (file.endsWith(".json") || file.endsWith(`${suffix}.json`)) {
4380
+ if (file === "datasources.json" || /^datasources\.\w+\.json$/.test(file)) {
4381
+ continue;
4382
+ }
4383
+ const result = await readJsonFile(filePath);
4384
+ if (result.error) {
4385
+ handleFileSystemError("read", result.error);
4386
+ continue;
4387
+ }
4388
+ datasources.push(...result.data);
4389
+ }
4390
+ }
4391
+ return {
4392
+ datasources
4393
+ };
4394
+ }
4395
+ async function readConsolidatedFiles(resolvedPath, suffix) {
4396
+ const datasourcesPath = join(resolvedPath, suffix ? `datasources.${suffix}.json` : "datasources.json");
4397
+ const datasourcesResult = await readJsonFile(datasourcesPath);
4398
+ if (datasourcesResult.error || !datasourcesResult.data.length) {
4399
+ throw new FileSystemError(
4400
+ "file_not_found",
4401
+ "read",
4402
+ datasourcesResult.error || new Error("Datasources file is empty"),
4403
+ `No datasources found in ${datasourcesPath}. Please make sure you have pulled the datasources first.`
4404
+ );
4405
+ }
4406
+ return {
4407
+ datasources: datasourcesResult.data
4408
+ };
4409
+ }
4410
+
4439
4411
  const program$2 = getProgram();
4440
4412
  datasourcesCommand.command("push [datasourceName]").description(`Push your space's datasources schema as json`).option("-f, --from <from>", "source space id").option("--fi, --filter <filter>", "glob filter to apply to the datasources before pushing").option("--sf, --separate-files", "Read from separate files instead of consolidated files").option("--su, --suffix <suffix>", "Suffix to add to the datasource name").action(async (datasourceName, options) => {
4441
4413
  konsola.title(`${commands.DATASOURCES}`, colorPalette.DATASOURCES, datasourceName ? `Pushing datasource ${datasourceName}...` : "Pushing datasources...");
@@ -4885,7 +4857,7 @@ program$1.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
4885
4857
  konsola.br();
4886
4858
  });
4887
4859
 
4888
- const version = "4.3.1";
4860
+ const version = "4.3.2";
4889
4861
  const pkg = {
4890
4862
  version: version};
4891
4863