storyblok 4.16.7 → 4.16.9

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
@@ -12,13 +12,13 @@ import { MultiBar, Presets } from 'cli-progress';
12
12
  import { Spinner } from '@topcli/spinner';
13
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, normalizeAssetUrl } from '@storyblok/management-api-client';
16
- import { RateLimit, Sema } from 'async-sema';
15
+ import { createManagementApiClient, normalizeAssetUrl } from '@storyblok/management-api-client';
17
16
  import { select, password, input, confirm } from '@inquirer/prompts';
18
17
  import { exec, spawn } from 'node:child_process';
19
18
  import { promisify } from 'node:util';
20
19
  import { minimatch } from 'minimatch';
21
20
  import { Readable, pipeline, Transform, Writable } from 'node:stream';
21
+ import { Sema } from 'async-sema';
22
22
  import { hash } from 'ohash';
23
23
  import { compile } from 'json-schema-to-typescript';
24
24
  import open from 'open';
@@ -381,6 +381,7 @@ function applyCliOverrides(commandChain, globalResolved, localResolved) {
381
381
  const value = command.getOptionValue(attrName);
382
382
  if (isRoot) {
383
383
  setValueAtPath(globalResolved, getOptionPath(option), value);
384
+ delete localResolved[attrName];
384
385
  } else {
385
386
  localResolved[attrName] = value;
386
387
  }
@@ -412,6 +413,11 @@ function parseNumber(value) {
412
413
  return parsed;
413
414
  }
414
415
  const GLOBAL_OPTION_DEFINITIONS = [
416
+ {
417
+ flags: "-p, --path <path>",
418
+ description: "Base directory for file storage (default: .storyblok)",
419
+ defaultValue: DEFAULT_GLOBAL_CONFIG.path
420
+ },
415
421
  {
416
422
  flags: "--verbose",
417
423
  description: "Enable verbose output",
@@ -674,9 +680,7 @@ const API_ACTIONS = {
674
680
  pull_asset_folder: "Failed to pull asset folder",
675
681
  pull_asset_folders: "Failed to pull asset folders",
676
682
  push_asset_folder: "Failed to push asset folder",
677
- push_asset_sign: "Failed to sign asset upload",
678
- push_asset_upload: "Failed to upload asset",
679
- push_asset_finish: "Failed to finish asset upload",
683
+ push_asset_create: "Failed to create asset",
680
684
  push_asset_update: "Failed to update asset",
681
685
  pull_datasources: "Failed to pull datasources",
682
686
  push_datasource: "Failed to push datasource",
@@ -1787,41 +1791,24 @@ function session() {
1787
1791
 
1788
1792
  let instance = null;
1789
1793
  let storedConfig = null;
1790
- let currentLimiterCapacity = Math.max(1, getActiveConfig().api.maxConcurrency);
1791
- let limiter = RateLimit(currentLimiterCapacity, { uniformDistribution: true });
1792
- function resolveLimiter() {
1793
- const desiredCapacity = Math.max(1, getActiveConfig().api.maxConcurrency);
1794
- if (desiredCapacity !== currentLimiterCapacity) {
1795
- limiter = RateLimit(desiredCapacity, { uniformDistribution: true });
1796
- currentLimiterCapacity = desiredCapacity;
1797
- }
1798
- return limiter;
1799
- }
1800
1794
  function configsAreEqual(config1, config2) {
1801
1795
  return JSON.stringify(config1) === JSON.stringify(config2);
1802
1796
  }
1803
- function applyRateLimit(client) {
1804
- if (getActiveConfig().api.maxConcurrency > 0) {
1805
- client.interceptors.request.use(async (request) => {
1806
- const limit = resolveLimiter();
1807
- await limit();
1808
- return request;
1809
- });
1810
- }
1811
- }
1812
- function creategetMapiClient(options) {
1813
- const client = new ManagementApiClient(options);
1814
- applyRateLimit(client);
1815
- return client;
1797
+ function createMapiClient(options) {
1798
+ const { api } = getActiveConfig();
1799
+ return createManagementApiClient({
1800
+ ...options,
1801
+ rateLimit: options.rateLimit ?? (api.maxConcurrency > 0 ? { maxConcurrency: api.maxConcurrency } : false)
1802
+ });
1816
1803
  }
1817
1804
  function getMapiClient(options) {
1818
1805
  if (!instance && options) {
1819
- instance = creategetMapiClient(options);
1806
+ instance = createMapiClient(options);
1820
1807
  storedConfig = options;
1821
1808
  } else if (!instance) {
1822
1809
  throw new Error("MAPI client not initialized. Call getMapiClient with configuration first.");
1823
1810
  } else if (options && storedConfig && !configsAreEqual(options, storedConfig)) {
1824
- instance = creategetMapiClient(options);
1811
+ instance = createMapiClient(options);
1825
1812
  storedConfig = options;
1826
1813
  }
1827
1814
  return instance;
@@ -1859,9 +1846,7 @@ function getProgram() {
1859
1846
  await initializeSession();
1860
1847
  if (state.password) {
1861
1848
  getMapiClient({
1862
- token: {
1863
- accessToken: state.password
1864
- },
1849
+ personalAccessToken: state.password,
1865
1850
  region: state.region ?? resolvedConfig.region
1866
1851
  });
1867
1852
  }
@@ -1938,10 +1923,8 @@ const getStoryblokUrl = (region = "eu") => {
1938
1923
 
1939
1924
  const getUser = async (token, region) => {
1940
1925
  try {
1941
- const client = creategetMapiClient({
1942
- token: {
1943
- accessToken: token
1944
- },
1926
+ const client = createMapiClient({
1927
+ personalAccessToken: token,
1945
1928
  region
1946
1929
  });
1947
1930
  const { data } = await client.users.me({
@@ -2281,7 +2264,7 @@ const fetchComponents = async (spaceId) => {
2281
2264
  const client = getMapiClient();
2282
2265
  const { data } = await client.components.list({
2283
2266
  path: {
2284
- space_id: spaceId
2267
+ space_id: Number(spaceId)
2285
2268
  },
2286
2269
  throwOnError: true
2287
2270
  });
@@ -2295,7 +2278,7 @@ const fetchComponent = async (spaceId, componentName) => {
2295
2278
  const client = getMapiClient();
2296
2279
  const { data } = await client.components.list({
2297
2280
  path: {
2298
- space_id: spaceId
2281
+ space_id: Number(spaceId)
2299
2282
  },
2300
2283
  query: {
2301
2284
  search: componentName
@@ -2312,7 +2295,7 @@ const fetchComponentGroups = async (spaceId) => {
2312
2295
  const client = getMapiClient();
2313
2296
  const { data } = await client.componentFolders.list({
2314
2297
  path: {
2315
- space_id: spaceId
2298
+ space_id: Number(spaceId)
2316
2299
  }
2317
2300
  });
2318
2301
  return data?.component_groups;
@@ -2325,7 +2308,7 @@ const fetchComponentPresets = async (spaceId) => {
2325
2308
  const client = getMapiClient();
2326
2309
  const { data } = await client.presets.list({
2327
2310
  path: {
2328
- space_id: spaceId
2311
+ space_id: Number(spaceId)
2329
2312
  }
2330
2313
  });
2331
2314
  return data?.presets;
@@ -2338,7 +2321,7 @@ const fetchComponentInternalTags = async (spaceId) => {
2338
2321
  const client = getMapiClient();
2339
2322
  const { data } = await client.internalTags.list({
2340
2323
  path: {
2341
- space_id: spaceId
2324
+ space_id: Number(spaceId)
2342
2325
  }
2343
2326
  });
2344
2327
  return data?.internal_tags?.filter((tag) => tag.object_type === "component");
@@ -2392,7 +2375,7 @@ const pushComponent = async (space, component) => {
2392
2375
  const client = getMapiClient();
2393
2376
  const { data } = await client.components.create({
2394
2377
  path: {
2395
- space_id: space
2378
+ space_id: Number(space)
2396
2379
  },
2397
2380
  body: {
2398
2381
  component
@@ -2406,10 +2389,9 @@ const pushComponent = async (space, component) => {
2406
2389
  const updateComponent = async (space, componentId, component) => {
2407
2390
  try {
2408
2391
  const client = getMapiClient();
2409
- const { data } = await client.components.update({
2392
+ const { data } = await client.components.update(componentId, {
2410
2393
  path: {
2411
- space_id: Number(space),
2412
- component_id: componentId
2394
+ space_id: Number(space)
2413
2395
  },
2414
2396
  body: {
2415
2397
  component
@@ -2448,10 +2430,9 @@ const pushComponentGroup = async (space, componentGroup) => {
2448
2430
  const updateComponentGroup = async (space, groupId, componentGroup) => {
2449
2431
  try {
2450
2432
  const client = getMapiClient();
2451
- const { data } = await client.componentFolders.update({
2433
+ const { data } = await client.componentFolders.update(groupId, {
2452
2434
  path: {
2453
- space_id: Number(space),
2454
- component_group_id: String(groupId)
2435
+ space_id: Number(space)
2455
2436
  },
2456
2437
  body: {
2457
2438
  component_group: componentGroup
@@ -2490,10 +2471,9 @@ const pushComponentPreset = async (space, preset) => {
2490
2471
  const updateComponentPreset = async (space, presetId, preset) => {
2491
2472
  try {
2492
2473
  const client = getMapiClient();
2493
- const { data } = await client.presets.update({
2474
+ const { data } = await client.presets.update(presetId, {
2494
2475
  path: {
2495
- space_id: Number(space),
2496
- preset_id: presetId
2476
+ space_id: Number(space)
2497
2477
  },
2498
2478
  body: {
2499
2479
  preset
@@ -2515,11 +2495,8 @@ const upsertComponentPreset = async (space, preset, existingId) => {
2515
2495
  const deleteComponentPreset = async (space, presetId) => {
2516
2496
  try {
2517
2497
  const client = getMapiClient();
2518
- await client.presets.delete({
2519
- path: {
2520
- space_id: Number(space),
2521
- preset_id: presetId
2522
- },
2498
+ await client.presets.delete(presetId, {
2499
+ path: { space_id: Number(space) },
2523
2500
  throwOnError: true
2524
2501
  });
2525
2502
  } catch (error) {
@@ -2544,10 +2521,9 @@ const pushComponentInternalTag = async (space, componentInternalTag) => {
2544
2521
  const updateComponentInternalTag = async (space, tagId, componentInternalTag) => {
2545
2522
  try {
2546
2523
  const client = getMapiClient();
2547
- const { data } = await client.internalTags.update({
2524
+ const { data } = await client.internalTags.update(tagId, {
2548
2525
  path: {
2549
- space_id: Number(space),
2550
- internal_tag_id: tagId
2526
+ space_id: Number(space)
2551
2527
  },
2552
2528
  body: componentInternalTag,
2553
2529
  throwOnError: true
@@ -2668,7 +2644,7 @@ async function readConsolidatedFiles$1(resolvedPath, suffix) {
2668
2644
  };
2669
2645
  }
2670
2646
 
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.`);
2647
+ 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").description(`Download your space's components schema as json. Optionally specify a component name to pull a single component.`);
2672
2648
  pullCmd$4.action(async (componentName, options, command) => {
2673
2649
  const ui = getUI();
2674
2650
  const logger = getLogger();
@@ -2794,6 +2770,9 @@ function buildDependencyGraph(context) {
2794
2770
  }
2795
2771
  const compositeKey = `${sourceComponent.name}:${preset.name}`;
2796
2772
  const targetPreset = spaceState.target.presets.get(compositeKey);
2773
+ if (targetPreset && targetPreset.id === void 0) {
2774
+ throw new Error(`Target preset "${preset.name}" is missing an id`);
2775
+ }
2797
2776
  const targetData = targetPreset ? {
2798
2777
  resource: targetPreset,
2799
2778
  id: targetPreset.id
@@ -3288,6 +3267,9 @@ class PresetNode {
3288
3267
  return result;
3289
3268
  }
3290
3269
  updateTargetData(result) {
3270
+ if (result.id === void 0) {
3271
+ throw new Error(`Preset "${this.name}" is missing an id after upsert`);
3272
+ }
3291
3273
  this.targetData = {
3292
3274
  resource: result,
3293
3275
  id: result.id
@@ -3351,8 +3333,8 @@ function collectAllDependencies(components, allComponents, allGroups, allTags) {
3351
3333
  const initialGroupUuids = Array.from(requiredGroupUuids);
3352
3334
  initialGroupUuids.forEach((groupUuid) => collectParentGroups(groupUuid));
3353
3335
  const filteredComponents = allComponents.filter((component) => requiredComponents.has(component.name));
3354
- const filteredGroups = allGroups.filter((group) => requiredGroupUuids.has(group.uuid));
3355
- const filteredTags = allTags.filter((tag) => requiredTagIds.has(tag.id));
3336
+ const filteredGroups = allGroups.filter((group) => group.uuid !== void 0 && requiredGroupUuids.has(group.uuid));
3337
+ const filteredTags = allTags.filter((tag) => tag.id !== void 0 && requiredTagIds.has(tag.id));
3356
3338
  return { filteredComponents, filteredGroups, filteredTags };
3357
3339
  }
3358
3340
  function filterSpaceDataByComponent(spaceData, componentName) {
@@ -3588,14 +3570,8 @@ function createMinimalStubComponent(name) {
3588
3570
  return {
3589
3571
  name,
3590
3572
  display_name: name,
3591
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
3592
- updated_at: (/* @__PURE__ */ new Date()).toISOString(),
3593
- id: 0,
3594
- // Will be set by API
3595
- schema: {},
3573
+ schema: {}
3596
3574
  // Minimal empty schema
3597
- internal_tags_list: [],
3598
- internal_tag_ids: []
3599
3575
  };
3600
3576
  }
3601
3577
  async function processLevel(level, graph, space, maxConcurrency) {
@@ -3697,7 +3673,7 @@ async function pushWithDependencyGraph(space, spaceState, maxConcurrency = getAc
3697
3673
  return results;
3698
3674
  }
3699
3675
 
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");
3676
+ 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");
3701
3677
  pushCmd$3.action(async (componentName, options, command) => {
3702
3678
  const ui = getUI();
3703
3679
  const logger = getLogger();
@@ -3865,7 +3841,7 @@ const fetchSpace = async (spaceId) => {
3865
3841
  const client = getMapiClient();
3866
3842
  const { data } = await client.spaces.get({
3867
3843
  path: {
3868
- space_id: spaceId
3844
+ space_id: Number(spaceId)
3869
3845
  },
3870
3846
  throwOnError: true
3871
3847
  });
@@ -3916,7 +3892,7 @@ const saveLanguagesToFile = async (space, internationalizationOptions, options)
3916
3892
 
3917
3893
  const program$9 = getProgram();
3918
3894
  const languagesCommand = program$9.command(commands.LANGUAGES).alias("lang").description(`Manage your space's languages`);
3919
- const pullCmd$3 = languagesCommand.command("pull").description(`Download your space's languages schema as json`).option("-f, --filename <filename>", "filename to save the file as <filename>.<suffix>.json").option("--su, --suffix <suffix>", "suffix to add to the file name (e.g. languages.<suffix>.json). By default, the space ID is used.").option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage");
3895
+ const pullCmd$3 = languagesCommand.command("pull").description(`Download your space's languages schema as json`).option("-f, --filename <filename>", "filename to save the file as <filename>.<suffix>.json").option("--su, --suffix <suffix>", "suffix to add to the file name (e.g. languages.<suffix>.json). By default, the space ID is used.").option("-s, --space <space>", "space ID");
3920
3896
  pullCmd$3.action(async (options, command) => {
3921
3897
  konsola.title(`${commands.LANGUAGES}`, colorPalette.LANGUAGES);
3922
3898
  const { space, path, verbose } = command.optsWithGlobals();
@@ -3980,7 +3956,7 @@ const getMigrationTemplate = () => {
3980
3956
  `;
3981
3957
  };
3982
3958
  const generateMigration = async (space, path, component, suffix) => {
3983
- const resolvedPath = path ? resolve(process.cwd(), path, "migrations", space) : resolvePath(path, `migrations/${space}`);
3959
+ const resolvedPath = resolvePath(path, `migrations/${space}`);
3984
3960
  const fileName = suffix ? `${component.name}.${suffix}.js` : `${component.name}.js`;
3985
3961
  const migrationPath = join(resolvedPath, fileName);
3986
3962
  try {
@@ -3990,7 +3966,7 @@ const generateMigration = async (space, path, component, suffix) => {
3990
3966
  }
3991
3967
  };
3992
3968
 
3993
- const generateCmd$1 = migrationsCommand.command("generate [componentName]").description("Generate a migration file").option("--su, --suffix <suffix>", "suffix to add to the file name (e.g. {component-name}.<suffix>.js)").option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage");
3969
+ const generateCmd$1 = migrationsCommand.command("generate [componentName]").description("Generate a migration file").option("--su, --suffix <suffix>", "suffix to add to the file name (e.g. {component-name}.<suffix>.js)").option("-s, --space <space>", "space ID");
3994
3970
  generateCmd$1.action(async (componentName, options, command) => {
3995
3971
  const ui = getUI();
3996
3972
  const logger = getLogger();
@@ -4046,7 +4022,7 @@ const fetchStories = async (spaceId, params) => {
4046
4022
  const client = getMapiClient();
4047
4023
  const { data, response } = await client.stories.list({
4048
4024
  path: {
4049
- space_id: spaceId
4025
+ space_id: Number(spaceId)
4050
4026
  },
4051
4027
  query: {
4052
4028
  ...params,
@@ -4056,7 +4032,7 @@ const fetchStories = async (spaceId, params) => {
4056
4032
  throwOnError: true
4057
4033
  });
4058
4034
  return {
4059
- stories: data?.stories || [],
4035
+ stories: data.stories || [],
4060
4036
  headers: response.headers
4061
4037
  };
4062
4038
  } catch (error) {
@@ -4066,10 +4042,9 @@ const fetchStories = async (spaceId, params) => {
4066
4042
  const fetchStory = async (spaceId, storyId) => {
4067
4043
  try {
4068
4044
  const client = getMapiClient();
4069
- const { data } = await client.stories.get({
4045
+ const { data } = await client.stories.get(storyId, {
4070
4046
  path: {
4071
- space_id: spaceId,
4072
- story_id: storyId
4047
+ space_id: Number(spaceId)
4073
4048
  },
4074
4049
  throwOnError: true
4075
4050
  });
@@ -4083,10 +4058,14 @@ const createStory = async (spaceId, payload) => {
4083
4058
  const client = getMapiClient();
4084
4059
  const { data } = await client.stories.create({
4085
4060
  path: {
4086
- space_id: spaceId
4061
+ space_id: Number(spaceId)
4087
4062
  },
4088
4063
  body: {
4089
- story: payload.story,
4064
+ story: {
4065
+ ...payload.story,
4066
+ // StoryCreate2 expects `parent_id?: number`; normalize null → undefined.
4067
+ parent_id: payload.story.parent_id ?? void 0
4068
+ },
4090
4069
  ...payload.publish ? { publish: payload.publish } : {}
4091
4070
  },
4092
4071
  throwOnError: true
@@ -4099,13 +4078,16 @@ const createStory = async (spaceId, payload) => {
4099
4078
  const updateStory = async (spaceId, storyId, payload) => {
4100
4079
  try {
4101
4080
  const client = getMapiClient();
4102
- const { data } = await client.stories.updateStory({
4081
+ const { data } = await client.stories.update(storyId, {
4103
4082
  path: {
4104
- space_id: spaceId,
4105
- story_id: storyId
4083
+ space_id: Number(spaceId)
4106
4084
  },
4107
4085
  body: {
4108
- story: payload.story,
4086
+ story: {
4087
+ ...payload.story,
4088
+ // StoryUpdate2 expects `parent_id?: number`; normalize null → undefined.
4089
+ parent_id: payload.story.parent_id ?? void 0
4090
+ },
4109
4091
  force_update: payload.force_update === "1" ? "1" : "0",
4110
4092
  ...payload.publish ? { publish: payload.publish } : {}
4111
4093
  },
@@ -4353,12 +4335,12 @@ function applyMigrationToAllBlocks(content, migrationFunction, targetComponent)
4353
4335
  }
4354
4336
  if (Array.isArray(content[key])) {
4355
4337
  for (const value of content[key]) {
4356
- if (value && typeof value === "object") {
4338
+ if (value && typeof value === "object" && "_uid" in value && "component" in value) {
4357
4339
  const blockProcessed = applyMigrationToAllBlocks(value, migrationFunction, targetComponent);
4358
4340
  processed = processed || blockProcessed;
4359
4341
  }
4360
4342
  }
4361
- } else if (content[key] && typeof content[key] === "object") {
4343
+ } else if (content[key] && typeof content[key] === "object" && "_uid" in content[key] && "component" in content[key]) {
4362
4344
  const blockProcessed = applyMigrationToAllBlocks(content[key], migrationFunction, targetComponent);
4363
4345
  processed = processed || blockProcessed;
4364
4346
  }
@@ -4774,7 +4756,7 @@ class UpdateStream extends Writable {
4774
4756
  }
4775
4757
  }
4776
4758
 
4777
- const runCmd = migrationsCommand.command("run [componentName]").description("Run migrations").option("--fi, --filter <filter>", "glob filter to apply to the components before pushing").option("-d, --dry-run", "Preview changes without applying them to Storyblok").option("-q, --query <query>", 'Filter stories by content attributes using Storyblok filter query syntax. Example: --query="[highlighted][in]=true"').option("--starts-with <path>", 'Filter stories by path. Example: --starts-with="/en/blog/"').option("--publish <publish>", "Options for publication mode: all | published | published-with-changes").option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage");
4759
+ const runCmd = migrationsCommand.command("run [componentName]").description("Run migrations").option("--fi, --filter <filter>", "glob filter to apply to the components before pushing").option("-d, --dry-run", "Preview changes without applying them to Storyblok").option("-q, --query <query>", 'Filter stories by content attributes using Storyblok filter query syntax. Example: --query="[highlighted][in]=true"').option("--starts-with <path>", 'Filter stories by path. Example: --starts-with="/en/blog/"').option("--publish <publish>", "Options for publication mode: all | published | published-with-changes").option("-s, --space <space>", "space ID");
4778
4760
  runCmd.action(async (componentName, options, command) => {
4779
4761
  const ui = getUI();
4780
4762
  const logger = getLogger();
@@ -4897,7 +4879,7 @@ runCmd.action(async (componentName, options, command) => {
4897
4879
  }
4898
4880
  });
4899
4881
 
4900
- const rollbackCmd = migrationsCommand.command("rollback [migrationFile]").description("Rollback a migration").option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage");
4882
+ const rollbackCmd = migrationsCommand.command("rollback [migrationFile]").description("Rollback a migration").option("-s, --space <space>", "space ID");
4901
4883
  rollbackCmd.action(async (migrationFile, _options, command) => {
4902
4884
  const ui = getUI();
4903
4885
  const logger = getLogger();
@@ -5532,11 +5514,6 @@ const getPropertyTypeAnnotation = (property, prefix, suffix) => {
5532
5514
  }
5533
5515
  if (property.source === "internal_stories") {
5534
5516
  if (property.filter_content_type) {
5535
- if (typeof property.filter_content_type === "string") {
5536
- return {
5537
- tsType: `(${getStoryType(property.filter_content_type, prefix, suffix)} | string )${property.type === "options" ? "[]" : ""}`
5538
- };
5539
- }
5540
5517
  return {
5541
5518
  tsType: `(${property.filter_content_type.map((type2) => getStoryType(type2, prefix, suffix)).join(" | ")} | string )${property.type === "options" ? "[]" : ""}`
5542
5519
  };
@@ -5891,7 +5868,7 @@ const pushDatasource = async (spaceId, datasource) => {
5891
5868
  const client = getMapiClient();
5892
5869
  const { data } = await client.datasources.create({
5893
5870
  path: {
5894
- space_id: spaceId
5871
+ space_id: Number(spaceId)
5895
5872
  },
5896
5873
  body: { datasource },
5897
5874
  throwOnError: true
@@ -5904,10 +5881,9 @@ const pushDatasource = async (spaceId, datasource) => {
5904
5881
  const updateDatasource = async (spaceId, datasourceId, datasource) => {
5905
5882
  try {
5906
5883
  const client = getMapiClient();
5907
- const { data } = await client.datasources.update({
5884
+ const { data } = await client.datasources.update(datasourceId, {
5908
5885
  path: {
5909
- space_id: spaceId,
5910
- datasource_id: datasourceId
5886
+ space_id: Number(spaceId)
5911
5887
  },
5912
5888
  body: {
5913
5889
  datasource
@@ -5931,11 +5907,12 @@ const pushDatasourceEntry = async (spaceId, datasourceId, entry, position) => {
5931
5907
  const client = getMapiClient();
5932
5908
  const { data } = await client.datasourceEntries.create({
5933
5909
  path: {
5934
- space_id: spaceId
5910
+ space_id: Number(spaceId)
5935
5911
  },
5936
5912
  body: {
5937
5913
  datasource_entry: {
5938
5914
  ...entry,
5915
+ value: entry.value ?? "",
5939
5916
  datasource_id: datasourceId,
5940
5917
  ...position != null && { position }
5941
5918
  }
@@ -5950,10 +5927,9 @@ const pushDatasourceEntry = async (spaceId, datasourceId, entry, position) => {
5950
5927
  const updateDatasourceEntry = async (spaceId, entryId, entry, position) => {
5951
5928
  try {
5952
5929
  const client = getMapiClient();
5953
- await client.datasourceEntries.updateDatasourceEntry({
5930
+ await client.datasourceEntries.update(entryId, {
5954
5931
  path: {
5955
- space_id: spaceId,
5956
- datasource_entry_id: entryId
5932
+ space_id: Number(spaceId)
5957
5933
  },
5958
5934
  body: {
5959
5935
  datasource_entry: {
@@ -5978,10 +5954,9 @@ const upsertDatasourceEntry = async (space, datasourceId, entry, existingId, pos
5978
5954
  const deleteDatasourceEntry = async (spaceId, entryId) => {
5979
5955
  try {
5980
5956
  const client = getMapiClient();
5981
- await client.datasourceEntries.delete({
5957
+ await client.datasourceEntries.delete(entryId, {
5982
5958
  path: {
5983
- space_id: spaceId,
5984
- datasource_entry_id: entryId
5959
+ space_id: Number(spaceId)
5985
5960
  },
5986
5961
  throwOnError: true
5987
5962
  });
@@ -6061,7 +6036,7 @@ async function readConsolidatedFiles(resolvedPath, suffix) {
6061
6036
  const generateCmd = typesCommand.command("generate").description("Generate types d.ts for your component schemas").option(
6062
6037
  "--filename <name>",
6063
6038
  "Base file name for all component types when generating a single declarations file (e.g. components.d.ts). Ignored when using --separate-files."
6064
- ).option("--sf, --separate-files", "Generate one .d.ts file per component instead of a single combined file").option("--strict", "strict mode, no loose typing").option("--type-prefix <prefix>", "prefix to be prepended to all generated component type names").option("--type-suffix <suffix>", "suffix to be appended to all generated component type names").option("--suffix <suffix>", "Components suffix").option("--custom-fields-parser <path>", "Path to the parser file for Custom Field Types").option("--compiler-options <options>", "path to the compiler options from json-schema-to-typescript").option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage");
6039
+ ).option("--sf, --separate-files", "Generate one .d.ts file per component instead of a single combined file").option("--strict", "strict mode, no loose typing").option("--type-prefix <prefix>", "prefix to be prepended to all generated component type names").option("--type-suffix <suffix>", "suffix to be appended to all generated component type names").option("--suffix <suffix>", "Components suffix").option("--custom-fields-parser <path>", "Path to the parser file for Custom Field Types").option("--compiler-options <options>", "path to the compiler options from json-schema-to-typescript").option("-s, --space <space>", "space ID");
6065
6040
  generateCmd.action(async (options, command) => {
6066
6041
  konsola.title(`${commands.TYPES}`, colorPalette.TYPES, "Generating types...");
6067
6042
  const { space, path, verbose, suffix, filename, separateFiles } = command.optsWithGlobals();
@@ -6144,7 +6119,7 @@ const fetchDatasourceEntries = async (spaceId, datasourceId) => {
6144
6119
  return await fetchAllPages(
6145
6120
  (page) => client.datasourceEntries.list({
6146
6121
  path: {
6147
- space_id: spaceId
6122
+ space_id: Number(spaceId)
6148
6123
  },
6149
6124
  query: {
6150
6125
  datasource_id: datasourceId,
@@ -6164,14 +6139,14 @@ const fetchDatasources = async (spaceId) => {
6164
6139
  const datasources = await fetchAllPages(
6165
6140
  (page) => client.datasources.list({
6166
6141
  path: {
6167
- space_id: spaceId
6142
+ space_id: Number(spaceId)
6168
6143
  },
6169
6144
  query: {
6170
6145
  page
6171
6146
  },
6172
6147
  throwOnError: true
6173
6148
  }),
6174
- (data) => data?.datasources || []
6149
+ (data) => data.datasources || []
6175
6150
  );
6176
6151
  const datasourcesWithEntries = await Promise.all(
6177
6152
  datasources.map(async (ds) => {
@@ -6192,14 +6167,14 @@ const fetchDatasource = async (spaceId, datasourceName) => {
6192
6167
  const client = getMapiClient();
6193
6168
  const { data } = await client.datasources.list({
6194
6169
  path: {
6195
- space_id: spaceId
6170
+ space_id: Number(spaceId)
6196
6171
  },
6197
6172
  query: {
6198
6173
  search: datasourceName
6199
6174
  },
6200
6175
  throwOnError: true
6201
6176
  });
6202
- const found = data?.datasources?.find((d) => d.name === datasourceName);
6177
+ const found = data.datasources?.find((d) => d.name === datasourceName);
6203
6178
  if (!found) {
6204
6179
  return void 0;
6205
6180
  }
@@ -6228,7 +6203,7 @@ const saveDatasourcesToFiles = async (space, datasources, options) => {
6228
6203
  }
6229
6204
  };
6230
6205
 
6231
- const pullCmd$2 = 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)").option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage").description("Pull datasources from your space");
6206
+ const pullCmd$2 = 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)").option("-s, --space <space>", "space ID").description("Pull datasources from your space");
6232
6207
  pullCmd$2.action(async (datasourceName, options, command) => {
6233
6208
  konsola.title(`${commands.DATASOURCES}`, colorPalette.DATASOURCES, datasourceName ? `Pulling datasource ${datasourceName}...` : "Pulling datasources...");
6234
6209
  const { space, path, verbose } = command.optsWithGlobals();
@@ -6302,7 +6277,7 @@ pullCmd$2.action(async (datasourceName, options, command) => {
6302
6277
  }
6303
6278
  });
6304
6279
 
6305
- const pushCmd$2 = 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").option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage");
6280
+ const pushCmd$2 = 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").option("-s, --space <space>", "space ID");
6306
6281
  pushCmd$2.action(async (datasourceName, options, command) => {
6307
6282
  konsola.title(`${commands.DATASOURCES}`, colorPalette.DATASOURCES, datasourceName ? `Pushing datasource ${datasourceName}...` : "Pushing datasources...");
6308
6283
  const { space, path, verbose } = command.optsWithGlobals();
@@ -6427,10 +6402,9 @@ pushCmd$2.action(async (datasourceName, options, command) => {
6427
6402
  async function deleteDatasource(spaceId, id) {
6428
6403
  try {
6429
6404
  const client = getMapiClient();
6430
- await client.datasources.delete({
6405
+ await client.datasources.delete(Number(id), {
6431
6406
  path: {
6432
- space_id: spaceId,
6433
- datasource_id: Number(id)
6407
+ space_id: Number(spaceId)
6434
6408
  },
6435
6409
  throwOnError: true
6436
6410
  });
@@ -6439,7 +6413,7 @@ async function deleteDatasource(spaceId, id) {
6439
6413
  }
6440
6414
  }
6441
6415
 
6442
- const deleteCmd = datasourcesCommand.command("delete [name]").description("Delete a datasource from your space by name or id").option("--id <id>", "Delete by datasource id instead of name").option("--force", "Skip confirmation prompt for deletion (useful for CI)").option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage");
6416
+ const deleteCmd = datasourcesCommand.command("delete [name]").description("Delete a datasource from your space by name or id").option("--id <id>", "Delete by datasource id instead of name").option("--force", "Skip confirmation prompt for deletion (useful for CI)").option("-s, --space <space>", "space ID");
6443
6417
  deleteCmd.action(async (name, options, command) => {
6444
6418
  konsola.title(
6445
6419
  `${commands.DATASOURCES}`,
@@ -6912,7 +6886,7 @@ program$5.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
6912
6886
  const program$4 = getProgram();
6913
6887
  const logsCommand = program$4.command(commands.LOGS).alias("lg").description(`Inspect and manage logs.`);
6914
6888
 
6915
- const listCmd$1 = logsCommand.command("list").description("List logs").option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage");
6889
+ const listCmd$1 = logsCommand.command("list").description("List logs").option("-s, --space <space>", "space ID");
6916
6890
  listCmd$1.action(async (_options, command) => {
6917
6891
  const { space, path } = command.optsWithGlobals();
6918
6892
  const ui = getUI();
@@ -6926,7 +6900,7 @@ listCmd$1.action(async (_options, command) => {
6926
6900
  ui.list(logFiles);
6927
6901
  });
6928
6902
 
6929
- const pruneCmd$1 = logsCommand.command("prune").description("Prune logs").option("--keep <number>", "Max number of log files to keep (default `0`, meaning remove all)", Number.parseInt, 0).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage");
6903
+ const pruneCmd$1 = logsCommand.command("prune").description("Prune logs").option("--keep <number>", "Max number of log files to keep (default `0`, meaning remove all)", Number.parseInt, 0).option("-s, --space <space>", "space ID");
6930
6904
  pruneCmd$1.action(async (options, command) => {
6931
6905
  const { space, path } = command.optsWithGlobals();
6932
6906
  const ui = getUI();
@@ -6938,7 +6912,7 @@ pruneCmd$1.action(async (options, command) => {
6938
6912
  const program$3 = getProgram();
6939
6913
  const reportsCommand = program$3.command(commands.REPORTS).alias("rp").description("Inspect and manage reports.");
6940
6914
 
6941
- const listCmd = reportsCommand.command("list").description("List reports").option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage");
6915
+ const listCmd = reportsCommand.command("list").description("List reports").option("-s, --space <space>", "space ID");
6942
6916
  listCmd.action(async (_options, command) => {
6943
6917
  const { space, path } = command.optsWithGlobals();
6944
6918
  const ui = getUI();
@@ -6952,7 +6926,7 @@ listCmd.action(async (_options, command) => {
6952
6926
  ui.list(reportFiles);
6953
6927
  });
6954
6928
 
6955
- const pruneCmd = reportsCommand.command("prune").description("Prune reports").option("--keep <number>", "Max number of report files to keep (default `0`, meaning remove all)", Number.parseInt, 0).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage");
6929
+ const pruneCmd = reportsCommand.command("prune").description("Prune reports").option("--keep <number>", "Max number of report files to keep (default `0`, meaning remove all)", Number.parseInt, 0).option("-s, --space <space>", "space ID");
6956
6930
  pruneCmd.action(async (options, command) => {
6957
6931
  const { space, path } = command.optsWithGlobals();
6958
6932
  const ui = getUI();
@@ -6969,7 +6943,7 @@ const fetchAssets = async ({ spaceId, params }) => {
6969
6943
  const client = getMapiClient();
6970
6944
  const { data, response } = await client.assets.list({
6971
6945
  path: {
6972
- space_id: spaceId
6946
+ space_id: Number(spaceId)
6973
6947
  },
6974
6948
  query: {
6975
6949
  ...params,
@@ -6985,7 +6959,6 @@ const fetchAssets = async ({ spaceId, params }) => {
6985
6959
  };
6986
6960
  } catch (maybeError) {
6987
6961
  handleAPIError("pull_assets", toError(maybeError));
6988
- throw maybeError;
6989
6962
  }
6990
6963
  };
6991
6964
  const downloadFile = async (filename) => {
@@ -7007,7 +6980,6 @@ const getSignedAssetUrl = async (filename, assetToken, region) => {
7007
6980
  return response.data.asset.signed_url;
7008
6981
  } catch (maybeError) {
7009
6982
  handleAPIError("pull_asset", toError(maybeError));
7010
- throw maybeError;
7011
6983
  }
7012
6984
  };
7013
6985
  const fetchAssetFolders = async ({ spaceId }) => {
@@ -7015,7 +6987,7 @@ const fetchAssetFolders = async ({ spaceId }) => {
7015
6987
  const client = getMapiClient();
7016
6988
  const { data, response } = await client.assetFolders.list({
7017
6989
  path: {
7018
- space_id: spaceId
6990
+ space_id: Number(spaceId)
7019
6991
  },
7020
6992
  throwOnError: true
7021
6993
  });
@@ -7025,7 +6997,6 @@ const fetchAssetFolders = async ({ spaceId }) => {
7025
6997
  };
7026
6998
  } catch (maybeError) {
7027
6999
  handleAPIError("pull_asset_folders", toError(maybeError));
7028
- throw maybeError;
7029
7000
  }
7030
7001
  };
7031
7002
  const createAssetFolder = async (folder, {
@@ -7035,7 +7006,7 @@ const createAssetFolder = async (folder, {
7035
7006
  const client = getMapiClient();
7036
7007
  const { data } = await client.assetFolders.create({
7037
7008
  path: {
7038
- space_id: spaceId
7009
+ space_id: Number(spaceId)
7039
7010
  },
7040
7011
  body: { asset_folder: folder },
7041
7012
  throwOnError: true
@@ -7047,18 +7018,16 @@ const createAssetFolder = async (folder, {
7047
7018
  return asset_folder;
7048
7019
  } catch (maybeError) {
7049
7020
  handleAPIError("push_asset_folder", toError(maybeError));
7050
- throw maybeError;
7051
7021
  }
7052
7022
  };
7053
- const updateAssetFolder = async (folder, {
7023
+ const updateAssetFolder = async (id, folder, {
7054
7024
  spaceId
7055
7025
  }) => {
7056
7026
  try {
7057
7027
  const client = getMapiClient();
7058
- await client.assetFolders.update({
7028
+ await client.assetFolders.update(id, {
7059
7029
  path: {
7060
- asset_folder_id: folder.id,
7061
- space_id: spaceId
7030
+ space_id: Number(spaceId)
7062
7031
  },
7063
7032
  body: { asset_folder: folder },
7064
7033
  throwOnError: true
@@ -7066,96 +7035,8 @@ const updateAssetFolder = async (folder, {
7066
7035
  return folder;
7067
7036
  } catch (maybeError) {
7068
7037
  handleAPIError("push_asset_folder", toError(maybeError));
7069
- throw maybeError;
7070
- }
7071
- };
7072
- const requestAssetUpload = async (asset, { spaceId }) => {
7073
- try {
7074
- const client = getMapiClient();
7075
- const { data } = await client.assets.upload({
7076
- path: {
7077
- space_id: spaceId
7078
- },
7079
- body: {
7080
- // @ts-expect-error Our types are wrong, id is optional but allowed.
7081
- id: asset.id,
7082
- filename: asset.short_filename,
7083
- asset_folder_id: asset.asset_folder_id ?? void 0,
7084
- is_private: asset.is_private
7085
- },
7086
- throwOnError: true
7087
- });
7088
- const signedUpload = data;
7089
- if (!signedUpload?.id || !signedUpload?.post_url || !signedUpload?.fields) {
7090
- throw new Error("Failed to request signed upload!");
7091
- }
7092
- return signedUpload;
7093
- } catch (maybeError) {
7094
- handleAPIError("push_asset_sign", toError(maybeError));
7095
- throw maybeError;
7096
- }
7097
- };
7098
- const uploadAssetToS3 = async (asset, fileBuffer, {
7099
- signedUpload
7100
- }) => {
7101
- if (!signedUpload?.id || !signedUpload?.post_url || !signedUpload?.fields) {
7102
- throw new Error("Invalid signed upload!");
7103
- }
7104
- const formData = new FormData();
7105
- for (const [key, value] of Object.entries(signedUpload.fields)) {
7106
- formData.append(key, value);
7107
- }
7108
- const contentType = signedUpload.fields["Content-Type"] || "application/octet-stream";
7109
- formData.append("file", new File([Buffer.from(fileBuffer)], asset.short_filename, { type: contentType }));
7110
- const response = await fetch(signedUpload.post_url, {
7111
- method: "POST",
7112
- body: formData
7113
- });
7114
- if (!response.ok) {
7115
- handleAPIError("push_asset_upload", new Error("Failed to upload asset to storage"));
7116
- return;
7117
- }
7118
- return response;
7119
- };
7120
- const finishAssetUpload = async (assetId, {
7121
- spaceId
7122
- }) => {
7123
- try {
7124
- const client = getMapiClient();
7125
- await client.assets.finalize({
7126
- path: {
7127
- space_id: spaceId,
7128
- signed_response_object_id: String(assetId)
7129
- },
7130
- throwOnError: true
7131
- });
7132
- const { data } = await client.assets.get({
7133
- path: {
7134
- space_id: spaceId,
7135
- asset_id: assetId
7136
- },
7137
- throwOnError: true
7138
- });
7139
- return data;
7140
- } catch (maybeError) {
7141
- handleAPIError("push_asset_finish", toError(maybeError));
7142
- throw maybeError;
7143
7038
  }
7144
7039
  };
7145
- const uploadAsset = async (asset, fileBuffer, { spaceId }) => {
7146
- const signed = await requestAssetUpload(asset, {
7147
- spaceId
7148
- });
7149
- const uploadResponse = await uploadAssetToS3(asset, fileBuffer, {
7150
- signedUpload: signed
7151
- });
7152
- if (!uploadResponse?.ok) {
7153
- throw new Error("Error uploading asset to S3!");
7154
- }
7155
- return finishAssetUpload(Number(signed.id), {
7156
- spaceId
7157
- });
7158
- };
7159
7040
  const sha256 = (data) => {
7160
7041
  const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
7161
7042
  return createHash("sha256").update(buffer).digest("hex");
@@ -7170,64 +7051,41 @@ const downloadAssetFile = async (asset, options) => {
7170
7051
  }
7171
7052
  return downloadFile(url);
7172
7053
  };
7173
- const updateAsset = async (asset, fileBuffer, {
7174
- spaceId
7175
- }) => {
7054
+ const updateAsset = async (id, asset, { spaceId, fileBuffer }) => {
7176
7055
  try {
7177
- const assetWithNewFilename = { ...asset };
7178
- if (fileBuffer) {
7179
- const uploadedAsset = await uploadAsset({
7180
- id: asset.id,
7181
- asset_folder_id: asset.asset_folder_id,
7182
- short_filename: asset.short_filename || basename(asset.filename)
7183
- }, fileBuffer, { spaceId });
7184
- assetWithNewFilename.filename = uploadedAsset.filename;
7185
- assetWithNewFilename.short_filename = uploadedAsset.short_filename;
7186
- }
7187
7056
  const client = getMapiClient();
7188
- await client.assets.update({
7189
- path: {
7190
- space_id: spaceId,
7191
- asset_id: assetWithNewFilename.id
7192
- },
7193
- body: {
7194
- asset: assetWithNewFilename
7195
- },
7196
- throwOnError: true
7197
- });
7198
- return assetWithNewFilename;
7057
+ const { short_filename, ...metadata } = asset;
7058
+ if (fileBuffer !== void 0) {
7059
+ if (!short_filename) {
7060
+ throw new Error("short_filename is required when replacing an asset file");
7061
+ }
7062
+ await client.assets.update(id, {
7063
+ path: { space_id: Number(spaceId) },
7064
+ body: { asset: metadata, short_filename },
7065
+ file: fileBuffer
7066
+ });
7067
+ } else {
7068
+ await client.assets.update(id, {
7069
+ path: { space_id: Number(spaceId) },
7070
+ body: { asset: metadata }
7071
+ });
7072
+ }
7199
7073
  } catch (maybeError) {
7200
7074
  handleAPIError("push_asset_update", toError(maybeError));
7201
- throw maybeError;
7202
7075
  }
7203
7076
  };
7204
7077
  const createAsset = async (asset, fileBuffer, { spaceId }) => {
7205
- const createdAsset = await uploadAsset({
7206
- asset_folder_id: asset.asset_folder_id,
7207
- short_filename: asset.short_filename,
7208
- alt: asset.alt,
7209
- title: asset.title,
7210
- copyright: asset.copyright,
7211
- source: asset.source,
7212
- is_private: asset.is_private
7213
- }, fileBuffer, { spaceId });
7214
- const hasUpdatableMetadata = Boolean(
7215
- asset.alt || asset.title || asset.copyright || asset.source || asset.is_private || asset.meta_data && Object.keys(asset.meta_data).length > 0
7216
- );
7217
- if (hasUpdatableMetadata) {
7218
- const updatedAsset = await updateAsset({
7219
- ...asset,
7220
- id: createdAsset.id,
7221
- filename: createdAsset.filename
7222
- }, null, {
7223
- spaceId
7078
+ try {
7079
+ const client = getMapiClient();
7080
+ const { id: _id, ...assetBody } = asset;
7081
+ return await client.assets.create({
7082
+ body: assetBody,
7083
+ file: fileBuffer,
7084
+ path: { space_id: Number(spaceId) }
7224
7085
  });
7225
- if (!updatedAsset) {
7226
- throw new Error("Updating the created asset failed!");
7227
- }
7228
- return updatedAsset;
7086
+ } catch (maybeError) {
7087
+ handleAPIError("push_asset_create", toError(maybeError));
7229
7088
  }
7230
- return createdAsset;
7231
7089
  };
7232
7090
 
7233
7091
  const parseAssetData = (raw) => {
@@ -7275,15 +7133,14 @@ const isRemoteSource = (assetBinaryPath) => {
7275
7133
  const isValidManifestEntry = (entry) => Boolean(typeof entry.old_id === "number" && typeof entry.new_id === "number" && entry.old_filename && entry.new_filename);
7276
7134
  const loadAssetMap = async (manifestFile) => {
7277
7135
  const manifest = await loadManifest(manifestFile);
7278
- return new Map([
7279
- ...manifest.filter(isValidManifestEntry).map((e) => [
7280
- Number(e.old_id),
7281
- {
7282
- old: { id: Number(e.old_id), filename: e.old_filename || "" },
7283
- new: { id: Number(e.new_id), filename: e.new_filename || "" }
7284
- }
7285
- ])
7136
+ const entries = manifest.filter(isValidManifestEntry).map((e) => [
7137
+ Number(e.old_id),
7138
+ {
7139
+ old: { id: Number(e.old_id), filename: e.old_filename || "" },
7140
+ new: { id: Number(e.new_id), filename: e.new_filename || "" }
7141
+ }
7286
7142
  ]);
7143
+ return new Map(entries);
7287
7144
  };
7288
7145
  const loadAssetFolderMap = async (manifestFile) => {
7289
7146
  const manifest = await loadManifest(manifestFile);
@@ -7543,12 +7400,11 @@ const makeCreateAssetFolderAPITransport = ({ spaceId }) => (folder) => createAss
7543
7400
  }, {
7544
7401
  spaceId
7545
7402
  });
7546
- const makeUpdateAssetFolderAPITransport = ({ spaceId }) => (folder) => updateAssetFolder(folder, { spaceId });
7403
+ const makeUpdateAssetFolderAPITransport = ({ spaceId }) => (id, folder) => updateAssetFolder(id, folder, { spaceId });
7547
7404
  const makeGetAssetFolderAPITransport = ({ spaceId }) => async (folderId) => {
7548
- const { data, response } = await getMapiClient().assetFolders.get({
7405
+ const { data, response } = await getMapiClient().assetFolders.get(folderId, {
7549
7406
  path: {
7550
- asset_folder_id: folderId,
7551
- space_id: spaceId
7407
+ space_id: Number(spaceId)
7552
7408
  }
7553
7409
  });
7554
7410
  if (!response.ok && response.status !== 404) {
@@ -7576,7 +7432,7 @@ const upsertAssetFolderStream = ({
7576
7432
  parent_id: remoteParentId
7577
7433
  };
7578
7434
  const existingRemoteFolder = await transports.getAssetFolder(remoteFolderId);
7579
- const newRemoteFolder = existingRemoteFolder ? await transports.updateAssetFolder({ ...upsertFolder, parent_id: remoteParentId !== null ? remoteParentId : void 0 }) : await transports.createAssetFolder(upsertFolder);
7435
+ const newRemoteFolder = existingRemoteFolder ? { id: remoteFolderId, ...await transports.updateAssetFolder(remoteFolderId, { ...upsertFolder, parent_id: remoteParentId !== null ? remoteParentId : void 0 }) } : await transports.createAssetFolder(upsertFolder);
7580
7436
  if (!maps.assetFolders.get(folder.id)) {
7581
7437
  await transports.appendAssetFolderManifest(folder, newRemoteFolder);
7582
7438
  }
@@ -7657,9 +7513,7 @@ const readSingleAssetStream = ({
7657
7513
  const makeCreateAssetAPITransport = ({ spaceId }) => (asset, fileBuffer) => createAsset(asset, fileBuffer, { spaceId });
7658
7514
  const makeUpdateAssetAPITransport = ({
7659
7515
  spaceId
7660
- }) => (asset, fileBuffer) => updateAsset(asset, fileBuffer, {
7661
- spaceId
7662
- });
7516
+ }) => (id, asset, fileBuffer) => updateAsset(id, asset, { spaceId, fileBuffer });
7663
7517
  const makeAppendAssetManifestFSTransport = ({ manifestFile }) => async (localAsset, remoteAsset) => {
7664
7518
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
7665
7519
  await appendToFile(manifestFile, JSON.stringify({
@@ -7679,10 +7533,9 @@ const makeAppendAssetFolderManifestFSTransport = ({ manifestFile }) => async (lo
7679
7533
  }));
7680
7534
  };
7681
7535
  const makeGetAssetAPITransport = ({ spaceId }) => async (assetId) => {
7682
- const { data, response } = await getMapiClient().assets.get({
7536
+ const { data, response } = await getMapiClient().assets.get(assetId, {
7683
7537
  path: {
7684
- space_id: spaceId,
7685
- asset_id: assetId
7538
+ space_id: Number(spaceId)
7686
7539
  }
7687
7540
  });
7688
7541
  if (!response.ok && response.status !== 404) {
@@ -7712,27 +7565,18 @@ const hasShortFilename = (a) => {
7712
7565
  return !!a && typeof a === "object" && "short_filename" in a && typeof a.short_filename === "string";
7713
7566
  };
7714
7567
  const isDataUnchanged = (localAsset, remoteAsset) => {
7715
- if (localAsset.asset_folder_id !== remoteAsset.asset_folder_id) {
7716
- return false;
7717
- }
7718
- if (localAsset.alt !== remoteAsset.alt || localAsset.title !== remoteAsset.title || localAsset.copyright !== remoteAsset.copyright || localAsset.source !== remoteAsset.source || localAsset.is_private !== remoteAsset.is_private) {
7719
- return false;
7720
- }
7721
- const localAssetMetadataEntries = Object.entries(localAsset.meta_data || {});
7722
- const remoteAssetMetadataEntries = Object.entries(remoteAsset.meta_data || {});
7723
- if (localAssetMetadataEntries.length !== remoteAssetMetadataEntries.length) {
7724
- return false;
7725
- }
7726
- const hasChanges = localAssetMetadataEntries.some(([k, v]) => remoteAsset.meta_data && remoteAsset.meta_data[k] !== v);
7727
- return !hasChanges;
7728
- };
7729
- const isAssetUnchanged = async (localAsset, remoteAsset, localFileBuffer, downloadAssetFileTransport) => {
7730
- const remoteFileBuffer = await downloadAssetFileTransport(remoteAsset);
7731
- const isFileUnchanged = sha256(localFileBuffer) === sha256(remoteFileBuffer);
7732
- if (!isFileUnchanged) {
7733
- return false;
7568
+ for (const key of Object.keys(localAsset)) {
7569
+ const local = localAsset[key];
7570
+ const remote = remoteAsset[key];
7571
+ if (typeof local === "object" || typeof remote === "object") {
7572
+ if (JSON.stringify(local) !== JSON.stringify(remote)) {
7573
+ return false;
7574
+ }
7575
+ } else if (local !== remote) {
7576
+ return false;
7577
+ }
7734
7578
  }
7735
- return isDataUnchanged(localAsset, remoteAsset);
7579
+ return true;
7736
7580
  };
7737
7581
  const makeDownloadAssetFileTransport = ({
7738
7582
  assetToken,
@@ -7753,22 +7597,30 @@ const processAsset = async ({
7753
7597
  let status;
7754
7598
  if (remoteAsset) {
7755
7599
  const updatePayload = {
7756
- ...remoteAsset,
7757
- ...localAsset,
7758
- id: remoteAsset.id,
7759
- asset_folder_id: remoteFolderId
7600
+ asset_folder_id: remoteFolderId,
7601
+ alt: "alt" in localAsset ? localAsset.alt : remoteAsset.alt,
7602
+ title: "title" in localAsset ? localAsset.title : remoteAsset.title,
7603
+ copyright: "copyright" in localAsset ? localAsset.copyright : remoteAsset.copyright,
7604
+ source: "source" in localAsset ? localAsset.source : remoteAsset.source,
7605
+ is_private: "is_private" in localAsset ? localAsset.is_private : remoteAsset.is_private,
7606
+ focus: "focus" in localAsset ? localAsset.focus : remoteAsset.focus,
7607
+ expire_at: "expire_at" in localAsset ? localAsset.expire_at : remoteAsset.expire_at,
7608
+ publish_at: "publish_at" in localAsset ? localAsset.publish_at : remoteAsset.publish_at,
7609
+ internal_tag_ids: "internal_tag_ids" in localAsset ? localAsset.internal_tag_ids : remoteAsset.internal_tag_ids,
7610
+ meta_data: "meta_data" in localAsset ? localAsset.meta_data : remoteAsset.meta_data
7760
7611
  };
7761
- const canSkip = await isAssetUnchanged(
7762
- updatePayload,
7763
- remoteAsset,
7764
- fileBuffer,
7765
- transports.downloadAssetFile
7766
- );
7767
- if (canSkip) {
7612
+ const remoteFileBuffer = await transports.downloadAssetFile(remoteAsset);
7613
+ const isFileUnchanged = sha256(fileBuffer) === sha256(remoteFileBuffer);
7614
+ if (isFileUnchanged && isDataUnchanged(updatePayload, remoteAsset)) {
7768
7615
  newRemoteAsset = remoteAsset;
7769
7616
  status = "skipped";
7770
7617
  } else {
7771
- newRemoteAsset = await transports.updateAsset(updatePayload, fileBuffer);
7618
+ await transports.updateAsset(
7619
+ remoteAsset.id,
7620
+ { ...updatePayload, short_filename: remoteAsset.short_filename },
7621
+ isFileUnchanged ? void 0 : fileBuffer
7622
+ );
7623
+ newRemoteAsset = { ...remoteAsset, ...updatePayload };
7772
7624
  status = "updated";
7773
7625
  }
7774
7626
  } else if (hasShortFilename(localAsset)) {
@@ -7833,7 +7685,7 @@ const upsertAssetStream = ({
7833
7685
  });
7834
7686
  };
7835
7687
 
7836
- const pullCmd$1 = assetsCommand.command("pull").option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage").option("-d, --dry-run", "Preview changes without applying them to Storyblok").option("-q, --query <query>", 'Filter assets using Storyblok filter query syntax. Example: --query="search=my-file.jpg&with_tags=tag1,tag2"').option("--asset-token <token>", "Asset token for accessing private assets").description(`Download your space's assets as local files.`);
7688
+ const pullCmd$1 = assetsCommand.command("pull").option("-s, --space <space>", "space ID").option("-d, --dry-run", "Preview changes without applying them to Storyblok").option("-q, --query <query>", 'Filter assets using Storyblok filter query syntax. Example: --query="search=my-file.jpg&with_tags=tag1,tag2"').option("--asset-token <token>", "Asset token for accessing private assets").description(`Download your space's assets as local files.`);
7837
7689
  pullCmd$1.action(async (options, command) => {
7838
7690
  const ui = getUI();
7839
7691
  const logger = getLogger();
@@ -8164,7 +8016,7 @@ const storyRefMapper = (story, { schemas, maps }) => {
8164
8016
  }) : story.content,
8165
8017
  id: Number(maps.stories?.get(story.id) ?? story.id),
8166
8018
  uuid: String(maps.stories?.get(story.uuid) ?? story.uuid),
8167
- parent_id: parentId != null ? Number(parentId) : null,
8019
+ parent_id: parentId != null ? Number(parentId) : 0,
8168
8020
  alternates
8169
8021
  };
8170
8022
  return {
@@ -8482,7 +8334,10 @@ const makeWriteStoryFSTransport = ({ directoryPath }) => async (story) => {
8482
8334
  return story;
8483
8335
  };
8484
8336
  const makeWriteStoryAPITransport = ({ spaceId, publish }) => (mappedLocalStory) => updateStory(spaceId, mappedLocalStory.id, {
8485
- story: mappedLocalStory,
8337
+ story: {
8338
+ ...mappedLocalStory,
8339
+ parent_id: mappedLocalStory.parent_id ?? void 0
8340
+ },
8486
8341
  publish: publish ?? (isStoryPublishedWithoutChanges(mappedLocalStory) ? 1 : 0)
8487
8342
  });
8488
8343
  const makeCleanupStoryFSTransport = ({ directoryPath, maps }) => {
@@ -8779,7 +8634,7 @@ const mapAssetReferencesInStoriesPipeline = async ({
8779
8634
  return Object.entries(summaries);
8780
8635
  };
8781
8636
 
8782
- const pushCmd$1 = assetsCommand.command("push").argument("[asset]", "path or URL of a single asset to push").option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage").option("-f, --from <from>", "source space id").option("--data <data>", "inline asset data as JSON").option("--short-filename <short-filename>", "override the asset filename").option("--folder <folderId>", "destination asset folder ID").option("--cleanup", "delete local assets and metadata after a successful push (note: does not cleanup manifests)").option("--update-stories", "update file references in stories if necessary", false).option("--asset-token <token>", "asset token for accessing private assets").option("-d, --dry-run", "Preview changes without applying them to Storyblok").description(`Push local assets to a Storyblok space.`);
8637
+ const pushCmd$1 = assetsCommand.command("push").argument("[asset]", "path or URL of a single asset to push").option("-s, --space <space>", "space ID").option("-f, --from <from>", "source space id").option("--data <data>", "inline asset data as JSON").option("--short-filename <short-filename>", "override the asset filename").option("--folder <folderId>", "destination asset folder ID").option("--cleanup", "delete local assets and metadata after a successful push (note: does not cleanup manifests)").option("--update-stories", "update file references in stories if necessary", false).option("--asset-token <token>", "asset token for accessing private assets").option("-d, --dry-run", "Preview changes without applying them to Storyblok").description(`Push local assets to a Storyblok space.`);
8783
8638
  pushCmd$1.action(async (assetInput, options, command) => {
8784
8639
  const ui = getUI();
8785
8640
  const logger = getLogger();
@@ -8818,7 +8673,7 @@ pushCmd$1.action(async (assetInput, options, command) => {
8818
8673
  const assetsDirectoryPath = resolveCommandPath(directories.assets, fromSpace, basePath);
8819
8674
  const assetFolderGetTransport = makeGetAssetFolderAPITransport({ spaceId: targetSpace });
8820
8675
  const assetFolderCreateTransport = options.dryRun ? async (folder) => folder : makeCreateAssetFolderAPITransport({ spaceId: targetSpace });
8821
- const assetFolderUpdateTransport = options.dryRun ? async (folder) => folder : makeUpdateAssetFolderAPITransport({ spaceId: targetSpace });
8676
+ const assetFolderUpdateTransport = options.dryRun ? async (_id, folder) => folder : makeUpdateAssetFolderAPITransport({ spaceId: targetSpace });
8822
8677
  const assetFolderManifestTransport = options.dryRun ? () => Promise.resolve() : makeAppendAssetFolderManifestFSTransport({ manifestFile: folderManifestFile });
8823
8678
  const cleanupAssetFolderTransport = options.cleanup && !options.dryRun ? makeCleanupAssetFolderFSTransport() : () => Promise.resolve();
8824
8679
  summaries.push(...await upsertAssetFoldersPipeline({
@@ -8849,7 +8704,8 @@ pushCmd$1.action(async (assetInput, options, command) => {
8849
8704
  }
8850
8705
  const getAssetTransport = makeGetAssetAPITransport({ spaceId: targetSpace });
8851
8706
  const createAssetTransport = options.dryRun ? async (asset) => asset : makeCreateAssetAPITransport({ spaceId: targetSpace });
8852
- const updateAssetTransport = options.dryRun ? async (asset) => asset : makeUpdateAssetAPITransport({ spaceId: targetSpace });
8707
+ const updateAssetTransport = options.dryRun ? async () => {
8708
+ } : makeUpdateAssetAPITransport({ spaceId: targetSpace });
8853
8709
  const downloadAssetFileTransport = makeDownloadAssetFileTransport({
8854
8710
  assetToken,
8855
8711
  region
@@ -8925,7 +8781,7 @@ pushCmd$1.action(async (assetInput, options, command) => {
8925
8781
  const program$1 = getProgram();
8926
8782
  const storiesCommand = program$1.command(commands.STORIES).description(`Manage your space's stories`);
8927
8783
 
8928
- const pullCmd = storiesCommand.command("pull").option("-s, --space <space>", "space ID").option("-d, --dry-run", "Preview changes without applying them to Storyblok").option("-p, --path <path>", "base path to store stories (default .storyblok)").option("-q, --query <query>", 'Filter stories by content attributes using Storyblok filter query syntax. Example: --query="[highlighted][in]=true"').option("--starts-with <path>", 'Filter stories by path. Example: --starts-with="/en/blog/"').description(`Download your space's stories as separate json files.`);
8784
+ const pullCmd = storiesCommand.command("pull").option("-s, --space <space>", "space ID").option("-d, --dry-run", "Preview changes without applying them to Storyblok").option("-q, --query <query>", 'Filter stories by content attributes using Storyblok filter query syntax. Example: --query="[highlighted][in]=true"').option("--starts-with <path>", 'Filter stories by path. Example: --starts-with="/en/blog/"').description(`Download your space's stories as separate json files.`);
8929
8785
  pullCmd.action(async (options, command) => {
8930
8786
  const ui = getUI();
8931
8787
  const logger = getLogger();
@@ -9035,7 +8891,7 @@ pullCmd.action(async (options, command) => {
9035
8891
  }
9036
8892
  });
9037
8893
 
9038
- const pushCmd = storiesCommand.command("push").option("-s, --space <space>", "space ID").option("-f, --from <from>", "source space id").option("-p, --path <path>", "base path for stories and components (default .storyblok)").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.`);
8894
+ 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.`);
9039
8895
  pushCmd.action(async (options, command) => {
9040
8896
  const ui = getUI();
9041
8897
  const logger = getLogger();
@@ -9057,8 +8913,9 @@ pushCmd.action(async (options, command) => {
9057
8913
  handleError(new CommandError(`Please provide the space as argument --space YOUR_SPACE_ID.`), verbose);
9058
8914
  return;
9059
8915
  }
8916
+ const pendingWarnings = [];
8917
+ const warnedPlugins = /* @__PURE__ */ new Set();
9060
8918
  const warnAboutCustomPlugins = (fields, story) => {
9061
- const warnedPlugins = /* @__PURE__ */ new Set();
9062
8919
  for (const field of fields) {
9063
8920
  if (field.type === "custom" && typeof field.field_type === "string") {
9064
8921
  if (warnedPlugins.has(field.field_type)) {
@@ -9066,19 +8923,19 @@ pushCmd.action(async (options, command) => {
9066
8923
  }
9067
8924
  warnedPlugins.add(field.field_type);
9068
8925
  const message = `The custom plugin "${field.field_type}" may contain references that require manual updates.`;
9069
- ui.warn(message);
8926
+ pendingWarnings.push(message);
9070
8927
  logger.warn(message, { storyId: story.uuid });
9071
8928
  }
9072
8929
  }
9073
8930
  };
8931
+ const missingSchemaWarnings = /* @__PURE__ */ new Set();
9074
8932
  const warnAboutMissingSchemas = (missingSchemas, story) => {
9075
- const missingSchemaWarnings = /* @__PURE__ */ new Set();
9076
8933
  for (const schemaName of missingSchemas) {
9077
8934
  if (missingSchemaWarnings.has(schemaName)) {
9078
8935
  continue;
9079
8936
  }
9080
8937
  const message = `The component "${schemaName}" was not found. Please run \`storyblok components pull\` to fetch the latest components.`;
9081
- ui.warn(message);
8938
+ pendingWarnings.push(message);
9082
8939
  logger.warn(message, { storyId: story.uuid });
9083
8940
  missingSchemaWarnings.add(schemaName);
9084
8941
  }
@@ -9181,7 +9038,7 @@ pushCmd.action(async (options, command) => {
9181
9038
  }
9182
9039
  if (summary.creationResults.failed > 0) {
9183
9040
  const message = `${summary.creationResults.failed} ${summary.creationResults.failed === 1 ? "story" : "stories"} failed to create. References to these stories will be left unmapped.`;
9184
- ui.warn(message);
9041
+ pendingWarnings.push(message);
9185
9042
  logger.warn(message);
9186
9043
  }
9187
9044
  await pipeline$1(
@@ -9253,6 +9110,9 @@ pushCmd.action(async (options, command) => {
9253
9110
  } finally {
9254
9111
  logger.info("Pushing stories finished", summary);
9255
9112
  ui.stopAllProgressBars();
9113
+ for (const warning of pendingWarnings) {
9114
+ ui.warn(warning);
9115
+ }
9256
9116
  const failedStories = Math.max(summary.creationResults.failed, summary.processResults.failed, summary.updateResults.failed);
9257
9117
  ui.info(`Push results: ${summary.creationResults.total} ${summary.creationResults.total === 1 ? "story" : "stories"} pushed, ${failedStories} ${failedStories === 1 ? "story" : "stories"} failed`);
9258
9118
  ui.list([