storyblok 4.4.0 → 4.5.0

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
@@ -7,6 +7,7 @@ import { Command } from 'commander';
7
7
  import { readPackageUp } from 'read-package-up';
8
8
  import { Spinner } from '@topcli/spinner';
9
9
  import { select, password, input, confirm } from '@inquirer/prompts';
10
+ import { ManagementApiClient } from '@storyblok/management-api-client';
10
11
  import fs, { mkdir, writeFile, readFile as readFile$1, access, readdir } from 'node:fs/promises';
11
12
  import path, { join, parse, resolve } from 'node:path';
12
13
  import filenamify from 'filenamify';
@@ -187,6 +188,7 @@ const API_ACTIONS = {
187
188
  update_datasource: "Failed to update datasource",
188
189
  delete_datasource: "Failed to delete datasource",
189
190
  create_space: "Failed to create space",
191
+ pull_spaces: "Failed to pull spaces",
190
192
  fetch_blueprints: "Failed to fetch blueprints from GitHub"
191
193
  };
192
194
  const API_ERRORS = {
@@ -427,19 +429,6 @@ function maskToken(token) {
427
429
  const maskedPart = "*".repeat(token.length - 4);
428
430
  return `${visiblePart}${maskedPart}`;
429
431
  }
430
- const objectToStringParams = (obj) => {
431
- return Object.entries(obj).reduce((acc, [key, value]) => {
432
- if (value === void 0) {
433
- return acc;
434
- }
435
- if (typeof value === "object" && value !== null) {
436
- acc[key] = JSON.stringify(value);
437
- } else {
438
- acc[key] = String(value);
439
- }
440
- return acc;
441
- }, {});
442
- };
443
432
  function createRegexFromGlob(pattern) {
444
433
  return new RegExp(`^${pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*")}$`);
445
434
  }
@@ -542,15 +531,75 @@ const getStoryblokUrl = (region = "eu") => {
542
531
  return `https://${managementApiRegions[region]}/${API_VERSION}`;
543
532
  };
544
533
 
545
- const loginWithToken = async (token, region) => {
534
+ let instance = null;
535
+ let storedConfig = null;
536
+ function configsAreEqual(config1, config2) {
537
+ return JSON.stringify(config1) === JSON.stringify(config2);
538
+ }
539
+ function mapiClient(options) {
540
+ if (!instance && options) {
541
+ instance = new ManagementApiClient(options);
542
+ storedConfig = options;
543
+ } else if (!instance) {
544
+ throw new Error("MAPI client not initialized. Call mapiClient with configuration first.");
545
+ } else if (options && storedConfig && !configsAreEqual(options, storedConfig)) {
546
+ instance = new ManagementApiClient(options);
547
+ storedConfig = options;
548
+ }
549
+ return instance;
550
+ }
551
+
552
+ const getUser = async (token, region) => {
546
553
  try {
547
- const url = getStoryblokUrl(region);
548
- return await customFetch(`${url}/users/me`, {
549
- headers: {
550
- Authorization: token
551
- }
554
+ const client = mapiClient({
555
+ token: {
556
+ accessToken: token
557
+ },
558
+ region
552
559
  });
560
+ const { data } = await client.users.me({
561
+ throwOnError: true
562
+ });
563
+ return data?.user;
553
564
  } catch (error) {
565
+ if (error instanceof FetchError) {
566
+ const status = error.response.status;
567
+ switch (status) {
568
+ case 401:
569
+ throw new APIError("unauthorized", "get_user", error, `The token provided ${chalk.bold(maskToken(token))} is invalid.
570
+ Please make sure you are using the correct token and try again.`);
571
+ default:
572
+ throw new APIError("network_error", "get_user", error);
573
+ }
574
+ }
575
+ if (typeof error === "string" && error === "Unauthorized") {
576
+ const mockFetchError = new FetchError("Non-JSON response", {
577
+ status: 401,
578
+ statusText: "Unauthorized",
579
+ data: null
580
+ });
581
+ throw new APIError("unauthorized", "get_user", mockFetchError, `The token provided ${chalk.bold(maskToken(token))} is invalid.
582
+ Please make sure you are using the correct token and try again.`);
583
+ }
584
+ if (typeof error === "object" && error !== null && Object.keys(error).length === 0) {
585
+ const mockFetchError = new FetchError("Network Error", {
586
+ status: 500,
587
+ statusText: "Internal Server Error",
588
+ data: null
589
+ });
590
+ throw new APIError("network_error", "get_user", mockFetchError);
591
+ }
592
+ throw new APIError("generic", "get_user", error);
593
+ }
594
+ };
595
+
596
+ const loginWithToken = async (token, region) => {
597
+ try {
598
+ return await getUser(token, region);
599
+ } catch (error) {
600
+ if (error instanceof APIError) {
601
+ throw error;
602
+ }
554
603
  if (error instanceof FetchError) {
555
604
  const status = error.response.status;
556
605
  switch (status) {
@@ -817,11 +866,13 @@ program$i.command(commands.LOGIN).description("Login to the Storyblok CLI").opti
817
866
  });
818
867
  }
819
868
  spinner.start(`Logging in with token`);
820
- const { user } = await loginWithToken(token, userRegion);
821
- updateSession(user.email, token, userRegion);
822
- await persistCredentials(userRegion);
823
- spinner.succeed();
824
- konsola.ok(`Successfully logged in to region ${chalk.hex(colorPalette.PRIMARY)(`${regionNames[userRegion]} (${userRegion})`)}. Welcome ${chalk.hex(colorPalette.PRIMARY)(user.friendly_name)}.`, true);
869
+ const user = await loginWithToken(token, userRegion);
870
+ if (user) {
871
+ updateSession(user.email, token, userRegion);
872
+ await persistCredentials(userRegion);
873
+ spinner.succeed();
874
+ konsola.ok(`Successfully logged in to region ${chalk.hex(colorPalette.PRIMARY)(`${regionNames[userRegion]} (${userRegion})`)}. Welcome ${chalk.hex(colorPalette.PRIMARY)(user.friendly_name)}.`, true);
875
+ }
825
876
  } catch (error) {
826
877
  spinner.failed();
827
878
  konsola.br();
@@ -852,11 +903,13 @@ program$i.command(commands.LOGIN).description("Login to the Storyblok CLI").opti
852
903
  });
853
904
  }
854
905
  spinner.start(`Logging in with token`);
855
- const { user } = await loginWithToken(userToken, userRegion);
906
+ const user = await loginWithToken(userToken, userRegion);
856
907
  spinner.succeed();
857
- updateSession(user.email, userToken, userRegion);
858
- await persistCredentials(userRegion);
859
- konsola.ok(`Successfully logged in to region ${chalk.hex(colorPalette.PRIMARY)(`${regionNames[userRegion]} (${userRegion})`)}. Welcome ${chalk.hex(colorPalette.PRIMARY)(user.friendly_name)}.`, true);
908
+ if (user) {
909
+ updateSession(user.email, userToken, userRegion);
910
+ await persistCredentials(userRegion);
911
+ konsola.ok(`Successfully logged in to region ${chalk.hex(colorPalette.PRIMARY)(`${regionNames[userRegion]} (${userRegion})`)}. Welcome ${chalk.hex(colorPalette.PRIMARY)(user.friendly_name)}.`, true);
912
+ }
860
913
  } else {
861
914
  const userEmail = await input({
862
915
  message: "Please enter your email address:",
@@ -983,30 +1036,6 @@ program$g.command(commands.SIGNUP).description("Sign up for Storyblok").action(a
983
1036
  konsola.br();
984
1037
  });
985
1038
 
986
- const getUser = async (token, region) => {
987
- try {
988
- const url = getStoryblokUrl(region);
989
- const response = await customFetch(`${url}/users/me`, {
990
- headers: {
991
- Authorization: token
992
- }
993
- });
994
- return response;
995
- } catch (error) {
996
- if (error instanceof FetchError) {
997
- const status = error.response.status;
998
- switch (status) {
999
- case 401:
1000
- throw new APIError("unauthorized", "get_user", error, `The token provided ${chalk.bold(maskToken(token))} is invalid.
1001
- Please make sure you are using the correct token and try again.`);
1002
- default:
1003
- throw new APIError("network_error", "get_user", error);
1004
- }
1005
- }
1006
- throw new APIError("generic", "get_user", error);
1007
- }
1008
- };
1009
-
1010
1039
  const program$f = getProgram();
1011
1040
  program$f.command(commands.USER).description("Get the current user").action(async () => {
1012
1041
  konsola.title(`${commands.USER}`, colorPalette.USER);
@@ -1024,12 +1053,14 @@ program$f.command(commands.USER).description("Get the current user").action(asyn
1024
1053
  if (!password || !region) {
1025
1054
  throw new Error("No password or region found");
1026
1055
  }
1027
- const { user } = await getUser(password, region);
1028
- spinner.succeed();
1029
- if (verbose) {
1030
- konsola.info(JSON.stringify(user, null, 2));
1056
+ const user = await getUser(password, region);
1057
+ if (user) {
1058
+ if (verbose) {
1059
+ konsola.info(JSON.stringify(user, null, 2));
1060
+ }
1061
+ spinner.succeed();
1062
+ konsola.ok(`Hi ${chalk.bold(user.friendly_name)}, you are currently logged in with ${chalk.hex(colorPalette.PRIMARY)(user.email)} on ${chalk.bold(region)} region`, true);
1031
1063
  }
1032
- konsola.ok(`Hi ${chalk.bold(user.friendly_name)}, you are currently logged in with ${chalk.hex(colorPalette.PRIMARY)(user.email)} on ${chalk.bold(region)} region`, true);
1033
1064
  } catch (error) {
1034
1065
  spinner.failed();
1035
1066
  handleError(error, true);
@@ -1062,196 +1093,72 @@ const resolveRegion = async (thisCommand) => {
1062
1093
  const program$e = getProgram();
1063
1094
  const componentsCommand = program$e.command(commands.COMPONENTS).alias("comp").description(`Manage your space's block schema`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/components").hook("preAction", resolveRegion);
1064
1095
 
1065
- let instance = null;
1066
- const createMapiClient = (options) => {
1067
- const baseHeaders = {
1068
- "Content-Type": "application/json",
1069
- "Authorization": options.token
1070
- };
1071
- const state = {
1072
- uuid: `mapi-client-${Math.random().toString(36).substring(2, 15)}`,
1073
- baseHeaders,
1074
- url: options.url || getStoryblokUrl(options.region),
1075
- maxRetries: options.maxRetries ?? 6,
1076
- baseDelay: options.baseDelay ?? 500,
1077
- freeze: false
1078
- };
1079
- const request = async (path, fetchOptions, attempt = 0, isRateLimitOwner = false) => {
1080
- if (state.freeze && !isRateLimitOwner) {
1081
- if (options?.verbose) {
1082
- console.log(`\u23F3 ${path} - Waiting for rate limit to be resolved`);
1083
- }
1084
- await new Promise((resolve) => {
1085
- const checkFreeze = setInterval(() => {
1086
- if (!state.freeze) {
1087
- clearInterval(checkFreeze);
1088
- resolve();
1089
- }
1090
- }, 50);
1091
- });
1092
- await delay(100 + Math.random() * 400);
1093
- return request(path, fetchOptions, attempt);
1094
- }
1095
- try {
1096
- if (options?.verbose) {
1097
- console.log(`${state.url}/${path} - Attempt ${attempt}`);
1098
- }
1099
- const requestData = {
1100
- path,
1101
- method: fetchOptions?.method || "GET",
1102
- headers: {
1103
- ...state.baseHeaders,
1104
- ...fetchOptions?.headers
1105
- },
1106
- body: fetchOptions?.body
1107
- };
1108
- options?.onRequest?.(requestData);
1109
- const res = await fetch(`${state.url}/${path}`, {
1110
- headers: requestData.headers,
1111
- ...fetchOptions
1112
- });
1113
- let data;
1114
- if (res.status === 204 || res.headers.get("content-length") === "0") {
1115
- data = null;
1116
- } else {
1117
- try {
1118
- data = await res.json();
1119
- } catch {
1120
- throw new FetchError("Non-JSON response", {
1121
- status: res.status,
1122
- statusText: res.statusText,
1123
- data: null
1124
- });
1125
- }
1126
- }
1127
- options?.onResponse?.({
1128
- path,
1129
- method: requestData.method,
1130
- status: res.status,
1131
- data,
1132
- attempt
1133
- });
1134
- if (res.ok) {
1135
- if (options?.verbose) {
1136
- console.log(`\u2705 ${path}`);
1137
- }
1138
- return {
1139
- data,
1140
- attempt
1141
- };
1142
- } else {
1143
- throw new FetchError("Request failed", {
1144
- status: res.status,
1145
- statusText: res.statusText,
1146
- data
1147
- });
1148
- }
1149
- } catch (error) {
1150
- if (error instanceof FetchError) {
1151
- if (error.response.status === 429 && attempt < state.maxRetries) {
1152
- if (options?.verbose) {
1153
- console.log(`\u274C ${path} - Rate limit exceeded`);
1154
- }
1155
- let isOwner = isRateLimitOwner;
1156
- if (!state.freeze) {
1157
- state.freeze = true;
1158
- isOwner = true;
1159
- }
1160
- const waitTime = state.baseDelay * 2 ** attempt + Math.random() * 100;
1161
- await delay(waitTime);
1162
- try {
1163
- const result = await request(path, fetchOptions, attempt + 1, isOwner);
1164
- return result;
1165
- } finally {
1166
- if (isOwner && state.freeze) {
1167
- state.freeze = false;
1168
- }
1169
- }
1170
- }
1171
- throw error;
1172
- }
1173
- if (state.freeze && isRateLimitOwner) {
1174
- state.freeze = false;
1175
- }
1176
- throw new FetchError(error instanceof Error ? error.message : String(error), {
1177
- status: 0,
1178
- statusText: "Network Error",
1179
- data: null
1180
- });
1181
- }
1182
- };
1183
- const get = async (path, fetchOptions) => {
1184
- return request(path, fetchOptions);
1185
- };
1186
- const post = async (path, fetchOptions) => {
1187
- return request(path, { ...fetchOptions, method: "POST" });
1188
- };
1189
- const put = async (path, fetchOptions) => {
1190
- return request(path, { ...fetchOptions, method: "PUT" });
1191
- };
1192
- const _delete = async (path, fetchOptions) => {
1193
- return request(path, { ...fetchOptions, method: "DELETE" });
1194
- };
1195
- instance = {
1196
- uuid: state.uuid,
1197
- get,
1198
- post,
1199
- put,
1200
- delete: _delete,
1201
- dispose: () => {
1202
- instance = null;
1203
- }
1204
- };
1205
- return instance;
1206
- };
1207
- function mapiClient(options) {
1208
- if (!instance) {
1209
- instance = createMapiClient(options ?? {});
1210
- }
1211
- return instance;
1212
- }
1213
-
1214
- const fetchComponents = async (space) => {
1096
+ const fetchComponents = async (spaceId) => {
1215
1097
  try {
1216
1098
  const client = mapiClient();
1217
- const { data } = await client.get(`spaces/${space}/components`, {});
1218
- return data.components;
1099
+ const { data } = await client.components.list({
1100
+ path: {
1101
+ space_id: spaceId
1102
+ },
1103
+ throwOnError: true
1104
+ });
1105
+ return data?.components;
1219
1106
  } catch (error) {
1220
1107
  handleAPIError("pull_components", error);
1221
1108
  }
1222
1109
  };
1223
- const fetchComponent = async (space, componentName) => {
1110
+ const fetchComponent = async (spaceId, componentName) => {
1224
1111
  try {
1225
1112
  const client = mapiClient();
1226
- const { data } = await client.get(`spaces/${space}/components?search=${encodeURIComponent(componentName)}`, {});
1227
- return data.components?.find((c) => c.name === componentName);
1113
+ const { data } = await client.components.list({
1114
+ path: {
1115
+ space_id: spaceId
1116
+ },
1117
+ query: {
1118
+ search: componentName
1119
+ },
1120
+ throwOnError: true
1121
+ });
1122
+ return data?.components?.find((c) => c.name === componentName);
1228
1123
  } catch (error) {
1229
1124
  handleAPIError("pull_components", error, `Failed to fetch component ${componentName}`);
1230
1125
  }
1231
1126
  };
1232
- const fetchComponentGroups = async (space) => {
1127
+ const fetchComponentGroups = async (spaceId) => {
1233
1128
  try {
1234
1129
  const client = mapiClient();
1235
- const { data } = await client.get(`spaces/${space}/component_groups`);
1236
- return data.component_groups;
1130
+ const { data } = await client.componentFolders.list({
1131
+ path: {
1132
+ space_id: spaceId
1133
+ }
1134
+ });
1135
+ return data?.component_groups;
1237
1136
  } catch (error) {
1238
1137
  handleAPIError("pull_component_groups", error);
1239
1138
  }
1240
1139
  };
1241
- const fetchComponentPresets = async (space) => {
1140
+ const fetchComponentPresets = async (spaceId) => {
1242
1141
  try {
1243
1142
  const client = mapiClient();
1244
- const { data } = await client.get(`spaces/${space}/presets`);
1245
- return data.presets;
1143
+ const { data } = await client.presets.list({
1144
+ path: {
1145
+ space_id: spaceId
1146
+ }
1147
+ });
1148
+ return data?.presets;
1246
1149
  } catch (error) {
1247
1150
  handleAPIError("pull_component_presets", error);
1248
1151
  }
1249
1152
  };
1250
- const fetchComponentInternalTags = async (space) => {
1153
+ const fetchComponentInternalTags = async (spaceId) => {
1251
1154
  try {
1252
1155
  const client = mapiClient();
1253
- const { data } = await client.get(`spaces/${space}/internal_tags`, {});
1254
- return data.internal_tags.filter((tag) => tag.object_type === "component");
1156
+ const { data } = await client.internalTags.list({
1157
+ path: {
1158
+ space_id: spaceId
1159
+ }
1160
+ });
1161
+ return data?.internal_tags?.filter((tag) => tag.object_type === "component");
1255
1162
  } catch (error) {
1256
1163
  handleAPIError("pull_component_internal_tags", error);
1257
1164
  }
@@ -1263,7 +1170,7 @@ const saveComponentsToFiles = async (space, spaceData, options) => {
1263
1170
  try {
1264
1171
  if (separateFiles) {
1265
1172
  for (const component of components) {
1266
- const sanitizedName = sanitizeFilename(component.name);
1173
+ const sanitizedName = sanitizeFilename(component.name || "");
1267
1174
  const componentFilePath = join(resolvedPath, suffix ? `${sanitizedName}.${suffix}.json` : `${sanitizedName}.json`);
1268
1175
  await saveToFile(componentFilePath, JSON.stringify(component, null, 2));
1269
1176
  const componentPresets = presets.filter((preset) => preset.component_id === component.id);
@@ -1300,10 +1207,15 @@ const saveComponentsToFiles = async (space, spaceData, options) => {
1300
1207
  const pushComponent = async (space, component) => {
1301
1208
  try {
1302
1209
  const client = mapiClient();
1303
- const { data } = await client.post(`spaces/${space}/components`, {
1304
- body: JSON.stringify(component)
1210
+ const { data } = await client.components.create({
1211
+ path: {
1212
+ space_id: space
1213
+ },
1214
+ body: {
1215
+ component
1216
+ }
1305
1217
  });
1306
- return data.component;
1218
+ return data?.component;
1307
1219
  } catch (error) {
1308
1220
  handleAPIError("push_component", error, `Failed to push component ${component.name}`);
1309
1221
  }
@@ -1311,10 +1223,17 @@ const pushComponent = async (space, component) => {
1311
1223
  const updateComponent = async (space, componentId, component) => {
1312
1224
  try {
1313
1225
  const client = mapiClient();
1314
- const { data } = await client.put(`spaces/${space}/components/${componentId}`, {
1315
- body: JSON.stringify(component)
1226
+ const { data } = await client.components.update({
1227
+ path: {
1228
+ space_id: Number(space),
1229
+ component_id: componentId
1230
+ },
1231
+ body: {
1232
+ component
1233
+ },
1234
+ throwOnError: true
1316
1235
  });
1317
- return data.component;
1236
+ return data?.component;
1318
1237
  } catch (error) {
1319
1238
  handleAPIError("update_component", error, `Failed to update component ${component.name}`);
1320
1239
  }
@@ -1329,10 +1248,16 @@ const upsertComponent = async (space, component, existingId) => {
1329
1248
  const pushComponentGroup = async (space, componentGroup) => {
1330
1249
  try {
1331
1250
  const client = mapiClient();
1332
- const { data } = await client.post(`spaces/${space}/component_groups`, {
1333
- body: JSON.stringify(componentGroup)
1251
+ const { data } = await client.componentFolders.create({
1252
+ path: {
1253
+ space_id: Number(space)
1254
+ },
1255
+ body: {
1256
+ component_group: componentGroup
1257
+ },
1258
+ throwOnError: true
1334
1259
  });
1335
- return data.component_group;
1260
+ return data?.component_group;
1336
1261
  } catch (error) {
1337
1262
  handleAPIError("push_component_group", error, `Failed to push component group ${componentGroup.name}`);
1338
1263
  }
@@ -1340,10 +1265,17 @@ const pushComponentGroup = async (space, componentGroup) => {
1340
1265
  const updateComponentGroup = async (space, groupId, componentGroup) => {
1341
1266
  try {
1342
1267
  const client = mapiClient();
1343
- const { data } = await client.put(`spaces/${space}/component_groups/${groupId}`, {
1344
- body: JSON.stringify(componentGroup)
1268
+ const { data } = await client.componentFolders.update({
1269
+ path: {
1270
+ space_id: Number(space),
1271
+ component_group_id: String(groupId)
1272
+ },
1273
+ body: {
1274
+ component_group: componentGroup
1275
+ },
1276
+ throwOnError: true
1345
1277
  });
1346
- return data.component_group;
1278
+ return data?.component_group;
1347
1279
  } catch (error) {
1348
1280
  handleAPIError("update_component_group", error, `Failed to update component group ${componentGroup.name}`);
1349
1281
  }
@@ -1355,41 +1287,57 @@ const upsertComponentGroup = async (space, group, existingId) => {
1355
1287
  return await pushComponentGroup(space, group);
1356
1288
  }
1357
1289
  };
1358
- const pushComponentPreset = async (space, componentPreset) => {
1290
+ const pushComponentPreset = async (space, preset) => {
1359
1291
  try {
1360
1292
  const client = mapiClient();
1361
- const { data } = await client.post(`spaces/${space}/presets`, {
1362
- body: JSON.stringify(componentPreset)
1293
+ const { data } = await client.presets.create({
1294
+ path: {
1295
+ space_id: Number(space)
1296
+ },
1297
+ body: {
1298
+ preset
1299
+ },
1300
+ throwOnError: true
1363
1301
  });
1364
- return data.preset;
1302
+ return data?.preset;
1365
1303
  } catch (error) {
1366
- handleAPIError("push_component_preset", error, `Failed to push component preset ${componentPreset.preset.name}`);
1304
+ handleAPIError("push_component_preset", error, `Failed to push component preset ${preset.name}`);
1367
1305
  }
1368
1306
  };
1369
- const updateComponentPreset = async (space, presetId, componentPreset) => {
1307
+ const updateComponentPreset = async (space, presetId, preset) => {
1370
1308
  try {
1371
1309
  const client = mapiClient();
1372
- const { data } = await client.put(`spaces/${space}/presets/${presetId}`, {
1373
- body: JSON.stringify(componentPreset)
1310
+ const { data } = await client.presets.update({
1311
+ path: {
1312
+ space_id: Number(space),
1313
+ preset_id: presetId
1314
+ },
1315
+ body: {
1316
+ preset
1317
+ },
1318
+ throwOnError: true
1374
1319
  });
1375
- return data.preset;
1320
+ return data?.preset;
1376
1321
  } catch (error) {
1377
- handleAPIError("update_component_preset", error, `Failed to update component preset ${componentPreset.preset.name}`);
1322
+ handleAPIError("update_component_preset", error, `Failed to update component preset ${preset.name}`);
1378
1323
  }
1379
1324
  };
1380
1325
  const upsertComponentPreset = async (space, preset, existingId) => {
1381
1326
  if (existingId) {
1382
- return await updateComponentPreset(space, existingId, { preset });
1327
+ return await updateComponentPreset(space, existingId, preset);
1383
1328
  } else {
1384
- return await pushComponentPreset(space, { preset });
1329
+ return await pushComponentPreset(space, preset);
1385
1330
  }
1386
1331
  };
1387
1332
  const pushComponentInternalTag = async (space, componentInternalTag) => {
1388
1333
  try {
1389
1334
  const client = mapiClient();
1390
- const { data } = await client.post(`spaces/${space}/internal_tags`, {
1391
- method: "POST",
1392
- body: JSON.stringify(componentInternalTag)
1335
+ const { data } = await client.internalTags.create({
1336
+ path: {
1337
+ space_id: Number(space)
1338
+ },
1339
+ body: componentInternalTag,
1340
+ throwOnError: true
1393
1341
  });
1394
1342
  return data.internal_tag;
1395
1343
  } catch (error) {
@@ -1399,9 +1347,13 @@ const pushComponentInternalTag = async (space, componentInternalTag) => {
1399
1347
  const updateComponentInternalTag = async (space, tagId, componentInternalTag) => {
1400
1348
  try {
1401
1349
  const client = mapiClient();
1402
- const { data } = await client.put(`spaces/${space}/internal_tags/${tagId}`, {
1403
- method: "PUT",
1404
- body: JSON.stringify(componentInternalTag)
1350
+ const { data } = await client.internalTags.update({
1351
+ path: {
1352
+ space_id: Number(space),
1353
+ internal_tag_id: tagId
1354
+ },
1355
+ body: componentInternalTag,
1356
+ throwOnError: true
1405
1357
  });
1406
1358
  return data.internal_tag;
1407
1359
  } catch (error) {
@@ -1536,7 +1488,9 @@ componentsCommand.command("pull [componentName]").option("-f, --filename <filena
1536
1488
  }
1537
1489
  const { password, region } = state;
1538
1490
  mapiClient({
1539
- token: password,
1491
+ token: {
1492
+ accessToken: password
1493
+ },
1540
1494
  region
1541
1495
  });
1542
1496
  const spinnerGroups = new Spinner({
@@ -2449,7 +2403,6 @@ function createMinimalStubComponent(name) {
2449
2403
  // Will be set by API
2450
2404
  schema: {},
2451
2405
  // Minimal empty schema
2452
- color: null,
2453
2406
  internal_tags_list: [],
2454
2407
  internal_tag_ids: []
2455
2408
  };
@@ -2575,12 +2528,15 @@ componentsCommand.command("push [componentName]").description(`Push your space's
2575
2528
  konsola.br();
2576
2529
  const { password, region } = state;
2577
2530
  let requestCount = 0;
2578
- mapiClient({
2579
- token: password,
2580
- region,
2581
- onRequest: (_request) => {
2582
- requestCount++;
2583
- }
2531
+ const client = mapiClient({
2532
+ token: {
2533
+ accessToken: password
2534
+ },
2535
+ region
2536
+ });
2537
+ client.interceptors.request.use((config) => {
2538
+ requestCount++;
2539
+ return config;
2584
2540
  });
2585
2541
  try {
2586
2542
  const componentsData = await readComponentsFiles({
@@ -2695,18 +2651,43 @@ componentsCommand.command("push [componentName]").description(`Push your space's
2695
2651
  }
2696
2652
  });
2697
2653
 
2698
- const fetchLanguages = async (space, token, region) => {
2654
+ const fetchSpace = async (spaceId) => {
2699
2655
  try {
2700
- const url = getStoryblokUrl(region);
2701
- const response = await customFetch(`${url}/spaces/${space}`, {
2702
- headers: {
2703
- Authorization: token
2656
+ const client = mapiClient();
2657
+ const { data } = await client.spaces.get({
2658
+ path: {
2659
+ space_id: spaceId
2660
+ },
2661
+ throwOnError: true
2662
+ });
2663
+ return data?.space;
2664
+ } catch (error) {
2665
+ handleAPIError("pull_spaces", error, `Failed to fetch space ${spaceId}`);
2666
+ }
2667
+ };
2668
+ const createSpace = async (space) => {
2669
+ try {
2670
+ const client = mapiClient();
2671
+ const { data } = await client.spaces.create({
2672
+ body: {
2673
+ space
2704
2674
  }
2705
2675
  });
2706
- return {
2707
- default_lang_name: response.space.default_lang_name,
2708
- languages: response.space.languages
2709
- };
2676
+ return data?.space;
2677
+ } catch (error) {
2678
+ handleAPIError("create_space", error, `Failed to create space ${space.name}`);
2679
+ }
2680
+ };
2681
+
2682
+ const fetchLanguages = async (spaceId) => {
2683
+ try {
2684
+ const space = await fetchSpace(spaceId);
2685
+ if (space?.default_lang_name !== void 0 && space?.languages?.length) {
2686
+ return {
2687
+ default_lang_name: space?.default_lang_name,
2688
+ languages: space?.languages
2689
+ };
2690
+ }
2710
2691
  } catch (error) {
2711
2692
  handleAPIError("pull_languages", error);
2712
2693
  }
@@ -2741,12 +2722,18 @@ languagesCommand.command("pull").description(`Download your space's languages sc
2741
2722
  return;
2742
2723
  }
2743
2724
  const { password, region } = state;
2725
+ mapiClient({
2726
+ token: {
2727
+ accessToken: password
2728
+ },
2729
+ region
2730
+ });
2744
2731
  const spinner = new Spinner({
2745
2732
  verbose: !isVitest
2746
2733
  });
2747
2734
  try {
2748
2735
  spinner.start(`Fetching ${chalk.hex(colorPalette.LANGUAGES)("languages")}`);
2749
- const internationalization = await fetchLanguages(space, password, region);
2736
+ const internationalization = await fetchLanguages(space);
2750
2737
  if (!internationalization || internationalization.languages?.length === 0) {
2751
2738
  spinner.failed();
2752
2739
  konsola.warn(`No languages found in the space ${space}`, true);
@@ -2819,7 +2806,9 @@ migrationsCommand.command("generate [componentName]").description("Generate a mi
2819
2806
  }
2820
2807
  const { password, region } = state;
2821
2808
  mapiClient({
2822
- token: password,
2809
+ token: {
2810
+ accessToken: password
2811
+ },
2823
2812
  region
2824
2813
  });
2825
2814
  const spinner = new Spinner({
@@ -2843,27 +2832,59 @@ migrationsCommand.command("generate [componentName]").description("Generate a mi
2843
2832
  }
2844
2833
  });
2845
2834
 
2846
- const fetchStories = async (space, params) => {
2835
+ const fetchStories = async (spaceId, params) => {
2847
2836
  try {
2848
2837
  const client = mapiClient();
2838
+ const { data, response } = await client.stories.list({
2839
+ path: {
2840
+ space_id: spaceId
2841
+ },
2842
+ query: {
2843
+ ...params,
2844
+ per_page: params?.per_page || 100,
2845
+ page: params?.page || 1
2846
+ },
2847
+ throwOnError: true
2848
+ });
2849
+ return {
2850
+ stories: data?.stories || [],
2851
+ headers: response.headers
2852
+ };
2853
+ } catch (error) {
2854
+ handleAPIError("pull_stories", error);
2855
+ }
2856
+ };
2857
+ const fetchAllStories = async (spaceId, params) => {
2858
+ try {
2849
2859
  const allStories = [];
2850
2860
  let currentPage = 1;
2851
2861
  let hasMorePages = true;
2852
2862
  const perPage = 100;
2853
2863
  while (hasMorePages) {
2854
- const { filter_query, ...restParams } = params || {};
2855
- const regularParams = new URLSearchParams({
2856
- ...objectToStringParams({ ...restParams, per_page: perPage }),
2857
- ...currentPage > 1 && { page: currentPage.toString() }
2858
- }).toString();
2859
- const queryString = filter_query ? `${regularParams ? `${regularParams}&` : ""}${filter_query}` : regularParams;
2860
- const endpoint = `spaces/${space}/stories${queryString ? `?${queryString}` : ""}`;
2861
- const { data } = await client.get(endpoint, {});
2862
- allStories.push(...data.stories);
2863
- hasMorePages = data.stories.length === perPage && data.stories.length > 0;
2864
- if (data.stories.length < perPage) {
2864
+ const result = await fetchStories(spaceId, {
2865
+ ...params,
2866
+ per_page: perPage,
2867
+ page: currentPage
2868
+ });
2869
+ if (!result) {
2865
2870
  break;
2866
2871
  }
2872
+ const { stories, headers } = result;
2873
+ if (stories && stories.length > 0) {
2874
+ allStories.push(...stories);
2875
+ const total = headers.get("Total");
2876
+ const perPageHeader = headers.get("Per-Page");
2877
+ if (total && perPageHeader) {
2878
+ const totalCount = Number(total);
2879
+ const perPageCount = Number(perPageHeader);
2880
+ const totalPages = Math.ceil(totalCount / perPageCount);
2881
+ hasMorePages = currentPage < totalPages;
2882
+ } else {
2883
+ hasMorePages = stories.length === perPage;
2884
+ }
2885
+ } else {
2886
+ hasMorePages = false;
2887
+ }
2867
2888
  currentPage++;
2868
2889
  }
2869
2890
  return allStories;
@@ -2871,7 +2892,7 @@ const fetchStories = async (space, params) => {
2871
2892
  handleAPIError("pull_stories", error);
2872
2893
  }
2873
2894
  };
2874
- async function fetchStoriesByComponent(spaceOptions, filterOptions) {
2895
+ async function fetchAllStoriesByComponent(spaceOptions, filterOptions) {
2875
2896
  const { spaceId } = spaceOptions;
2876
2897
  const { componentName = "", query, starts_with } = filterOptions || {};
2877
2898
  const params = {
@@ -2884,30 +2905,43 @@ async function fetchStoriesByComponent(spaceOptions, filterOptions) {
2884
2905
  params.filter_query = query.startsWith("filter_query") ? query : `filter_query${query}`;
2885
2906
  }
2886
2907
  try {
2887
- const stories = await fetchStories(spaceId, params);
2908
+ const stories = await fetchAllStories(spaceId, params);
2888
2909
  return stories ?? [];
2889
2910
  } catch (error) {
2890
2911
  handleAPIError("pull_stories", error);
2891
2912
  }
2892
2913
  }
2893
- const fetchStory = async (space, storyId) => {
2914
+ const fetchStory = async (spaceId, storyId) => {
2894
2915
  try {
2895
2916
  const client = mapiClient();
2896
- const endpoint = `spaces/${space}/stories/${storyId}`;
2897
- const { data } = await client.get(endpoint, {});
2898
- return data.story;
2917
+ const { data } = await client.stories.get({
2918
+ path: {
2919
+ space_id: spaceId,
2920
+ story_id: storyId
2921
+ },
2922
+ throwOnError: true
2923
+ });
2924
+ return data?.story;
2899
2925
  } catch (error) {
2900
2926
  handleAPIError("pull_story", error);
2901
2927
  }
2902
2928
  };
2903
- const updateStory = async (space, storyId, payload) => {
2929
+ const updateStory = async (spaceId, storyId, payload) => {
2904
2930
  try {
2905
2931
  const client = mapiClient();
2906
- const endpoint = `spaces/${space}/stories/${storyId}`;
2907
- const { data } = await client.put(endpoint, {
2908
- body: JSON.stringify(payload)
2932
+ const { data } = await client.stories.updateStory({
2933
+ path: {
2934
+ space_id: spaceId,
2935
+ story_id: storyId
2936
+ },
2937
+ body: {
2938
+ story: payload.story,
2939
+ force_update: payload.force_update === "1" ? "1" : "0",
2940
+ publish: payload.publish
2941
+ },
2942
+ throwOnError: true
2909
2943
  });
2910
- return data.story;
2944
+ return data?.story;
2911
2945
  } catch (error) {
2912
2946
  handleAPIError("update_story", error);
2913
2947
  }
@@ -3224,7 +3258,9 @@ migrationsCommand.command("run [componentName]").description("Run migrations").o
3224
3258
  }
3225
3259
  const { password, region } = state;
3226
3260
  mapiClient({
3227
- token: password,
3261
+ token: {
3262
+ accessToken: password
3263
+ },
3228
3264
  region
3229
3265
  });
3230
3266
  try {
@@ -3249,7 +3285,7 @@ migrationsCommand.command("run [componentName]").description("Run migrations").o
3249
3285
  }
3250
3286
  spinner.succeed(`Found ${filteredMigrations.length} migration files.`);
3251
3287
  const storiesSpinner = new Spinner({ verbose: !isVitest }).start(`Fetching stories...`);
3252
- const stories = await fetchStoriesByComponent(
3288
+ const stories = await fetchAllStoriesByComponent(
3253
3289
  {
3254
3290
  spaceId: space
3255
3291
  },
@@ -3303,7 +3339,7 @@ migrationsCommand.command("run [componentName]").description("Run migrations").o
3303
3339
  const originalStory = validStories.find((s) => s.id === result.storyId);
3304
3340
  storiesByIdMap.set(result.storyId, {
3305
3341
  id: result.storyId,
3306
- name: result.name,
3342
+ name: result.name || "",
3307
3343
  content: result.content,
3308
3344
  published: originalStory?.published,
3309
3345
  published_at: originalStory?.published_at || void 0,
@@ -3923,12 +3959,16 @@ const getComponentPropertiesTypeAnnotations = async (component, options, spaceDa
3923
3959
  if (key.startsWith("tab-")) {
3924
3960
  return acc;
3925
3961
  }
3926
- const propertyType = value.type;
3962
+ if (!value || typeof value !== "object" || !("type" in value)) {
3963
+ return acc;
3964
+ }
3965
+ const schema = value;
3966
+ const propertyType = schema.type;
3927
3967
  const propertyTypeAnnotation = {
3928
- [key]: getPropertyTypeAnnotation(value, options.typePrefix, options.typeSuffix)
3968
+ [key]: getPropertyTypeAnnotation(schema, options.typePrefix, options.typeSuffix)
3929
3969
  };
3930
3970
  if (propertyType === "custom" && customFieldsParser) {
3931
- const customField = typeof customFieldsParser === "function" ? customFieldsParser(key, value) : {};
3971
+ const customField = typeof customFieldsParser === "function" ? customFieldsParser(key, schema) : {};
3932
3972
  return {
3933
3973
  ...acc,
3934
3974
  ...customField
@@ -3940,17 +3980,17 @@ const getComponentPropertiesTypeAnnotations = async (component, options, spaceDa
3940
3980
  }
3941
3981
  if (propertyType === "multilink") {
3942
3982
  const excludedLinktypes = [
3943
- ...!value.email_link_type ? ['{ linktype?: "email" }'] : [],
3944
- ...!value.asset_link_type ? ['{ linktype?: "asset" }'] : []
3983
+ ...!schema.email_link_type ? ['{ linktype?: "email" }'] : [],
3984
+ ...!schema.asset_link_type ? ['{ linktype?: "asset" }'] : []
3945
3985
  ];
3946
3986
  const componentType = toPascalCase(toCamelCase(propertyType));
3947
3987
  propertyTypeAnnotation[key].tsType = excludedLinktypes.length > 0 ? `Exclude<Storyblok${componentType}, ${excludedLinktypes.join(" | ")}>` : componentType;
3948
3988
  }
3949
3989
  if (propertyType === "bloks") {
3950
- if (value.restrict_components) {
3951
- if (value.restrict_type === "groups") {
3952
- if (Array.isArray(value.component_group_whitelist) && value.component_group_whitelist.length > 0) {
3953
- const componentsInGroupWhitelist = value.component_group_whitelist.reduce(
3990
+ if (schema.restrict_components) {
3991
+ if (schema.restrict_type === "groups") {
3992
+ if (Array.isArray(schema.component_group_whitelist) && schema.component_group_whitelist.length > 0) {
3993
+ const componentsInGroupWhitelist = schema.component_group_whitelist.reduce(
3954
3994
  (components, groupUUID) => {
3955
3995
  const componentsInGroup = spaceData.components.filter(
3956
3996
  (component2) => component2.component_group_uuid === groupUUID
@@ -3964,18 +4004,18 @@ const getComponentPropertiesTypeAnnotations = async (component, options, spaceDa
3964
4004
  );
3965
4005
  propertyTypeAnnotation[key].tsType = componentsInGroupWhitelist.length > 0 ? `(${componentsInGroupWhitelist.join(" | ")})[]` : `never[]`;
3966
4006
  }
3967
- } else if (value.restrict_type === "tags") {
3968
- if (Array.isArray(value.component_tag_whitelist) && value.component_tag_whitelist.length > 0) {
4007
+ } else if (schema.restrict_type === "tags") {
4008
+ if (Array.isArray(schema.component_tag_whitelist) && schema.component_tag_whitelist.length > 0) {
3969
4009
  const componentsWithTags = spaceData.components.filter(
3970
4010
  (component2) => component2.internal_tag_ids && component2.internal_tag_ids.some(
3971
- (tagId) => value.component_tag_whitelist.includes(Number(tagId))
4011
+ (tagId) => schema.component_tag_whitelist.includes(Number(tagId))
3972
4012
  )
3973
4013
  );
3974
4014
  propertyTypeAnnotation[key].tsType = componentsWithTags.length > 0 ? `(${componentsWithTags.map((component2) => getComponentType(component2.name, options)).join(" | ")})[]` : `never[]`;
3975
4015
  }
3976
4016
  } else {
3977
- if (Array.isArray(value.component_whitelist) && value.component_whitelist.length > 0) {
3978
- propertyTypeAnnotation[key].tsType = `(${value.component_whitelist.map((name) => getComponentType(name, options)).join(" | ")})[]`;
4017
+ if (Array.isArray(schema.component_whitelist) && schema.component_whitelist.length > 0) {
4018
+ propertyTypeAnnotation[key].tsType = `(${schema.component_whitelist.map((name) => getComponentType(name, options)).join(" | ")})[]`;
3979
4019
  }
3980
4020
  }
3981
4021
  } else {
@@ -4024,7 +4064,7 @@ const generateTypes = async (spaceData, options = {
4024
4064
  const componentPropertiesTypeAnnotations = await getComponentPropertiesTypeAnnotations(component, options, spaceData, customFieldsParser);
4025
4065
  const requiredFields = Object.entries(component?.schema || {}).reduce(
4026
4066
  (acc, [key, value]) => {
4027
- if (value.required) {
4067
+ if (value && typeof value === "object" && "required" in value && value.required) {
4028
4068
  return [...acc, key];
4029
4069
  }
4030
4070
  return acc;
@@ -4163,41 +4203,65 @@ typesCommand.command("generate").description("Generate types d.ts for your compo
4163
4203
  const program$4 = getProgram();
4164
4204
  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);
4165
4205
 
4166
- const fetchDatasourceEntries = async (space, datasourceId) => {
4206
+ const fetchDatasourceEntries = async (spaceId, datasourceId) => {
4167
4207
  try {
4168
4208
  const client = mapiClient();
4169
- const { data } = await client.get(`spaces/${space}/datasource_entries?datasource_id=${datasourceId}`);
4170
- return data.datasource_entries;
4209
+ const { data } = await client.datasourceEntries.list({
4210
+ path: {
4211
+ space_id: spaceId
4212
+ },
4213
+ query: {
4214
+ datasource_id: datasourceId
4215
+ },
4216
+ throwOnError: true
4217
+ });
4218
+ return data?.datasource_entries;
4171
4219
  } catch (error) {
4172
4220
  handleAPIError("pull_datasources", error);
4173
4221
  }
4174
4222
  };
4175
- const fetchDatasources = async (space) => {
4223
+ const fetchDatasources = async (spaceId) => {
4176
4224
  try {
4177
4225
  const client = mapiClient();
4178
- const { data } = await client.get(`spaces/${space}/datasources`);
4179
- const datasources = data.datasources;
4226
+ const { data } = await client.datasources.list({
4227
+ path: {
4228
+ space_id: spaceId
4229
+ },
4230
+ throwOnError: true
4231
+ });
4232
+ const datasources = data?.datasources;
4180
4233
  const datasourcesWithEntries = await Promise.all(
4181
- datasources.map(async (ds) => {
4182
- const entries = await fetchDatasourceEntries(space, ds.id);
4234
+ datasources?.map(async (ds) => {
4235
+ if (!ds.id) {
4236
+ return { ...ds, entries: [] };
4237
+ }
4238
+ const entries = await fetchDatasourceEntries(spaceId, ds.id);
4183
4239
  return { ...ds, entries };
4184
- })
4240
+ }) || []
4185
4241
  );
4186
4242
  return datasourcesWithEntries;
4187
4243
  } catch (error) {
4188
4244
  handleAPIError("pull_datasources", error);
4189
4245
  }
4190
4246
  };
4191
- const fetchDatasource = async (space, datasourceName) => {
4247
+ const fetchDatasource = async (spaceId, datasourceName) => {
4192
4248
  try {
4193
4249
  const client = mapiClient();
4194
- const { data } = await client.get(`spaces/${space}/datasources?search=${encodeURIComponent(datasourceName)}`);
4195
- const found = data.datasources?.find((d) => d.name === datasourceName);
4250
+ const { data } = await client.datasources.list({
4251
+ path: {
4252
+ space_id: spaceId
4253
+ },
4254
+ query: {
4255
+ search: datasourceName
4256
+ },
4257
+ throwOnError: true
4258
+ });
4259
+ const found = data?.datasources?.find((d) => d.name === datasourceName);
4196
4260
  if (!found) {
4197
4261
  return void 0;
4198
4262
  }
4199
- const entries = await fetchDatasourceEntries(space, found.id);
4200
- return { ...found, entries };
4263
+ const entries = await fetchDatasourceEntries(spaceId, found.id);
4264
+ return { ...found, entries: entries || [] };
4201
4265
  } catch (error) {
4202
4266
  handleAPIError("pull_datasources", error, `Failed to fetch datasource ${datasourceName}`);
4203
4267
  }
@@ -4208,7 +4272,7 @@ const saveDatasourcesToFiles = async (space, datasources, options) => {
4208
4272
  try {
4209
4273
  if (separateFiles) {
4210
4274
  for (const datasource of datasources) {
4211
- const sanitizedName = sanitizeFilename(datasource.name);
4275
+ const sanitizedName = sanitizeFilename(datasource.name || "");
4212
4276
  const datasourceFilePath = join(resolvedPath, suffix ? `${sanitizedName}.${suffix}.json` : `${sanitizedName}.json`);
4213
4277
  await saveToFile(datasourceFilePath, JSON.stringify(datasource, null, 2));
4214
4278
  }
@@ -4238,7 +4302,9 @@ datasourcesCommand.command("pull [datasourceName]").option("-f, --filename <file
4238
4302
  }
4239
4303
  const { password, region } = state;
4240
4304
  mapiClient({
4241
- token: password,
4305
+ token: {
4306
+ accessToken: password
4307
+ },
4242
4308
  region
4243
4309
  });
4244
4310
  const spinnerDatasources = new Spinner({
@@ -4291,22 +4357,33 @@ datasourcesCommand.command("pull [datasourceName]").option("-f, --filename <file
4291
4357
  }
4292
4358
  });
4293
4359
 
4294
- const pushDatasource = async (space, datasource) => {
4360
+ const pushDatasource = async (spaceId, datasource) => {
4295
4361
  try {
4296
4362
  const client = mapiClient();
4297
- const { data } = await client.post(`spaces/${space}/datasources`, {
4298
- body: JSON.stringify(datasource)
4363
+ const { data } = await client.datasources.create({
4364
+ path: {
4365
+ space_id: spaceId
4366
+ },
4367
+ body: { datasource },
4368
+ throwOnError: true
4299
4369
  });
4300
4370
  return data.datasource;
4301
4371
  } catch (error) {
4302
4372
  handleAPIError("push_datasource", error, `Failed to push datasource ${datasource.name}`);
4303
4373
  }
4304
4374
  };
4305
- const updateDatasource = async (space, datasourceId, datasource) => {
4375
+ const updateDatasource = async (spaceId, datasourceId, datasource) => {
4306
4376
  try {
4307
4377
  const client = mapiClient();
4308
- const { data } = await client.put(`spaces/${space}/datasources/${datasourceId}`, {
4309
- body: JSON.stringify(datasource)
4378
+ const { data } = await client.datasources.update({
4379
+ path: {
4380
+ space_id: spaceId,
4381
+ datasource_id: datasourceId
4382
+ },
4383
+ body: {
4384
+ datasource
4385
+ },
4386
+ throwOnError: true
4310
4387
  });
4311
4388
  return data.datasource;
4312
4389
  } catch (error) {
@@ -4320,29 +4397,38 @@ const upsertDatasource = async (space, datasource, existingId) => {
4320
4397
  return await pushDatasource(space, datasource);
4321
4398
  }
4322
4399
  };
4323
- const pushDatasourceEntry = async (space, datasourceId, entry) => {
4400
+ const pushDatasourceEntry = async (spaceId, datasourceId, entry) => {
4324
4401
  try {
4325
4402
  const client = mapiClient();
4326
- const { data } = await client.post(`spaces/${space}/datasource_entries`, {
4327
- body: JSON.stringify({
4403
+ const { data } = await client.datasourceEntries.create({
4404
+ path: {
4405
+ space_id: spaceId
4406
+ },
4407
+ body: {
4328
4408
  datasource_entry: {
4329
4409
  ...entry,
4330
4410
  datasource_id: datasourceId
4331
4411
  }
4332
- })
4412
+ },
4413
+ throwOnError: true
4333
4414
  });
4334
4415
  return data.datasource_entry;
4335
4416
  } catch (error) {
4336
4417
  handleAPIError("push_datasource", error, `Failed to push datasource entry ${entry.name}`);
4337
4418
  }
4338
4419
  };
4339
- const updateDatasourceEntry = async (space, entryId, entry) => {
4420
+ const updateDatasourceEntry = async (spaceId, entryId, entry) => {
4340
4421
  try {
4341
4422
  const client = mapiClient();
4342
- await client.put(`spaces/${space}/datasource_entries/${entryId}`, {
4343
- body: JSON.stringify({
4423
+ await client.datasourceEntries.updateDatasourceEntry({
4424
+ path: {
4425
+ space_id: spaceId,
4426
+ datasource_entry_id: entryId
4427
+ },
4428
+ body: {
4344
4429
  datasource_entry: entry
4345
- })
4430
+ },
4431
+ throwOnError: true
4346
4432
  });
4347
4433
  } catch (error) {
4348
4434
  handleAPIError("update_datasource", error, `Failed to update datasource entry ${entry.name}`);
@@ -4447,7 +4533,9 @@ datasourcesCommand.command("push [datasourceName]").description(`Push your space
4447
4533
  konsola.br();
4448
4534
  const { password, region } = state;
4449
4535
  mapiClient({
4450
- token: password,
4536
+ token: {
4537
+ accessToken: password
4538
+ },
4451
4539
  region
4452
4540
  });
4453
4541
  try {
@@ -4506,8 +4594,7 @@ datasourcesCommand.command("push [datasourceName]").description(`Push your space
4506
4594
  for (const entry of entries) {
4507
4595
  const existingEntryId = existingDatasource?.entries?.find((e) => e.name === entry.name)?.id;
4508
4596
  try {
4509
- const { id, ...entryData } = entry;
4510
- await upsertDatasourceEntry(space, result.id, entryData, existingEntryId);
4597
+ await upsertDatasourceEntry(space, result.id, entry, existingEntryId);
4511
4598
  } catch (entryError) {
4512
4599
  results.failed.push({ name: datasource.name, error: entryError });
4513
4600
  spinner.failed(`${chalk.hex(colorPalette.DATASOURCES)(datasource.name)} - Failed in ${spinner.elapsedTime.toFixed(2)}ms`);
@@ -4535,12 +4622,18 @@ datasourcesCommand.command("push [datasourceName]").description(`Push your space
4535
4622
  }
4536
4623
  });
4537
4624
 
4538
- async function deleteDatasource(space, id) {
4625
+ async function deleteDatasource(spaceId, id) {
4539
4626
  try {
4540
4627
  const client = mapiClient();
4541
- await client.delete(`spaces/${space}/datasources/${id}`);
4628
+ await client.datasources.delete({
4629
+ path: {
4630
+ space_id: spaceId,
4631
+ datasource_id: Number(id)
4632
+ },
4633
+ throwOnError: true
4634
+ });
4542
4635
  } catch (error) {
4543
- handleAPIError("delete_datasource", error, `Datasource with id '${id}' not found in space ${space}.`);
4636
+ handleAPIError("delete_datasource", error, `Datasource with id '${id}' not found in space ${spaceId}.`);
4544
4637
  }
4545
4638
  }
4546
4639
 
@@ -4568,7 +4661,9 @@ datasourcesCommand.command("delete [name]").description("Delete a datasource fro
4568
4661
  }
4569
4662
  const { password, region } = state;
4570
4663
  mapiClient({
4571
- token: password,
4664
+ token: {
4665
+ accessToken: password
4666
+ },
4572
4667
  region
4573
4668
  });
4574
4669
  const spinner = new Spinner({
@@ -4735,18 +4830,6 @@ const fetchBlueprintRepositories = async () => {
4735
4830
  }
4736
4831
  };
4737
4832
 
4738
- const createSpace = async (space) => {
4739
- try {
4740
- const client = mapiClient();
4741
- const { data } = await client.post("spaces", {
4742
- body: JSON.stringify(space)
4743
- });
4744
- return data.space;
4745
- } catch (error) {
4746
- handleAPIError("create_space", error, `Failed to create space ${space.name}`);
4747
- }
4748
- };
4749
-
4750
4833
  const program$1 = getProgram();
4751
4834
  program$1.command(`${commands.CREATE} [project-path]`).alias("c").description(`Scaffold a new project using Storyblok`).option("-t, --template <template>", "technology starter template").option("-b, --blueprint <blueprint>", "[DEPRECATED] use --template instead").option("--skip-space", "skip space creation").action(async (projectPath, options) => {
4752
4835
  konsola.title(`${commands.CREATE}`, colorPalette.CREATE);
@@ -4766,7 +4849,9 @@ program$1.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
4766
4849
  }
4767
4850
  const { password, region } = state;
4768
4851
  mapiClient({
4769
- token: password,
4852
+ token: {
4853
+ accessToken: password
4854
+ },
4770
4855
  region
4771
4856
  });
4772
4857
  const spinnerBlueprints = new Spinner({
@@ -4777,7 +4862,10 @@ program$1.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
4777
4862
  });
4778
4863
  let userData;
4779
4864
  try {
4780
- const { user } = await getUser(password, region);
4865
+ const user = await getUser(password, region);
4866
+ if (!user) {
4867
+ throw new Error("User data is undefined");
4868
+ }
4781
4869
  userData = user;
4782
4870
  } catch (error) {
4783
4871
  konsola.error("Failed to fetch user info. Please login again.", error);
@@ -4843,7 +4931,7 @@ program$1.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
4843
4931
  { name: "My personal account", value: "personal" }
4844
4932
  ];
4845
4933
  if (userData.has_org) {
4846
- choices.push({ name: `Organization (${userData.org.name})`, value: "org" });
4934
+ choices.push({ name: `Organization (${userData?.org?.name})`, value: "org" });
4847
4935
  }
4848
4936
  if (userData.has_partner) {
4849
4937
  choices.push({ name: "Partner Portal", value: "partner" });
@@ -4910,7 +4998,7 @@ program$1.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
4910
4998
  konsola.ok(`Your ${chalk.hex(colorPalette.PRIMARY)(technologyTemplate)} project is ready \u{1F389} !`);
4911
4999
  if (createdSpace?.first_token) {
4912
5000
  if (whereToCreateSpace === "org") {
4913
- konsola.ok(`Storyblok space created in organization ${chalk.hex(colorPalette.PRIMARY)(userData.org.name)}, preview url and .env configured automatically. You can now open your space in the browser at ${chalk.hex(colorPalette.PRIMARY)(generateSpaceUrl(createdSpace.id, region))}`);
5001
+ konsola.ok(`Storyblok space created in organization ${chalk.hex(colorPalette.PRIMARY)(userData?.org?.name)}, preview url and .env configured automatically. You can now open your space in the browser at ${chalk.hex(colorPalette.PRIMARY)(generateSpaceUrl(createdSpace.id, region))}`);
4914
5002
  } else if (whereToCreateSpace === "partner") {
4915
5003
  konsola.ok(`Storyblok space created in partner portal, preview url and .env configured automatically. You can now open your space in the browser at ${chalk.hex(colorPalette.PRIMARY)(generateSpaceUrl(createdSpace.id, region))}`);
4916
5004
  } else {
@@ -4932,7 +5020,7 @@ program$1.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
4932
5020
  konsola.br();
4933
5021
  });
4934
5022
 
4935
- const version = "4.4.0";
5023
+ const version = "4.5.0";
4936
5024
  const pkg = {
4937
5025
  version: version};
4938
5026