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 +454 -366
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -5
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
|
-
|
|
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
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
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
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
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
|
|
906
|
+
const user = await loginWithToken(userToken, userRegion);
|
|
856
907
|
spinner.succeed();
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
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
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
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
|
-
|
|
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.
|
|
1218
|
-
|
|
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 (
|
|
1110
|
+
const fetchComponent = async (spaceId, componentName) => {
|
|
1224
1111
|
try {
|
|
1225
1112
|
const client = mapiClient();
|
|
1226
|
-
const { data } = await client.
|
|
1227
|
-
|
|
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 (
|
|
1127
|
+
const fetchComponentGroups = async (spaceId) => {
|
|
1233
1128
|
try {
|
|
1234
1129
|
const client = mapiClient();
|
|
1235
|
-
const { data } = await client.
|
|
1236
|
-
|
|
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 (
|
|
1140
|
+
const fetchComponentPresets = async (spaceId) => {
|
|
1242
1141
|
try {
|
|
1243
1142
|
const client = mapiClient();
|
|
1244
|
-
const { data } = await client.
|
|
1245
|
-
|
|
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 (
|
|
1153
|
+
const fetchComponentInternalTags = async (spaceId) => {
|
|
1251
1154
|
try {
|
|
1252
1155
|
const client = mapiClient();
|
|
1253
|
-
const { data } = await client.
|
|
1254
|
-
|
|
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.
|
|
1304
|
-
|
|
1210
|
+
const { data } = await client.components.create({
|
|
1211
|
+
path: {
|
|
1212
|
+
space_id: space
|
|
1213
|
+
},
|
|
1214
|
+
body: {
|
|
1215
|
+
component
|
|
1216
|
+
}
|
|
1305
1217
|
});
|
|
1306
|
-
return data
|
|
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.
|
|
1315
|
-
|
|
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
|
|
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.
|
|
1333
|
-
|
|
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
|
|
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.
|
|
1344
|
-
|
|
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
|
|
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,
|
|
1290
|
+
const pushComponentPreset = async (space, preset) => {
|
|
1359
1291
|
try {
|
|
1360
1292
|
const client = mapiClient();
|
|
1361
|
-
const { data } = await client.
|
|
1362
|
-
|
|
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
|
|
1302
|
+
return data?.preset;
|
|
1365
1303
|
} catch (error) {
|
|
1366
|
-
handleAPIError("push_component_preset", error, `Failed to push component preset ${
|
|
1304
|
+
handleAPIError("push_component_preset", error, `Failed to push component preset ${preset.name}`);
|
|
1367
1305
|
}
|
|
1368
1306
|
};
|
|
1369
|
-
const updateComponentPreset = async (space, presetId,
|
|
1307
|
+
const updateComponentPreset = async (space, presetId, preset) => {
|
|
1370
1308
|
try {
|
|
1371
1309
|
const client = mapiClient();
|
|
1372
|
-
const { data } = await client.
|
|
1373
|
-
|
|
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
|
|
1320
|
+
return data?.preset;
|
|
1376
1321
|
} catch (error) {
|
|
1377
|
-
handleAPIError("update_component_preset", error, `Failed to update component preset ${
|
|
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,
|
|
1327
|
+
return await updateComponentPreset(space, existingId, preset);
|
|
1383
1328
|
} else {
|
|
1384
|
-
return await pushComponentPreset(space,
|
|
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.
|
|
1391
|
-
|
|
1392
|
-
|
|
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.
|
|
1403
|
-
|
|
1404
|
-
|
|
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:
|
|
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:
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
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
|
|
2654
|
+
const fetchSpace = async (spaceId) => {
|
|
2699
2655
|
try {
|
|
2700
|
-
const
|
|
2701
|
-
const
|
|
2702
|
-
|
|
2703
|
-
|
|
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
|
-
|
|
2708
|
-
|
|
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
|
|
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:
|
|
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 (
|
|
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
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
})
|
|
2859
|
-
|
|
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
|
|
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
|
|
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 (
|
|
2914
|
+
const fetchStory = async (spaceId, storyId) => {
|
|
2894
2915
|
try {
|
|
2895
2916
|
const client = mapiClient();
|
|
2896
|
-
const
|
|
2897
|
-
|
|
2898
|
-
|
|
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 (
|
|
2929
|
+
const updateStory = async (spaceId, storyId, payload) => {
|
|
2904
2930
|
try {
|
|
2905
2931
|
const client = mapiClient();
|
|
2906
|
-
const
|
|
2907
|
-
|
|
2908
|
-
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
|
|
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(
|
|
3968
|
+
[key]: getPropertyTypeAnnotation(schema, options.typePrefix, options.typeSuffix)
|
|
3929
3969
|
};
|
|
3930
3970
|
if (propertyType === "custom" && customFieldsParser) {
|
|
3931
|
-
const customField = typeof customFieldsParser === "function" ? customFieldsParser(key,
|
|
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
|
-
...!
|
|
3944
|
-
...!
|
|
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 (
|
|
3951
|
-
if (
|
|
3952
|
-
if (Array.isArray(
|
|
3953
|
-
const componentsInGroupWhitelist =
|
|
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 (
|
|
3968
|
-
if (Array.isArray(
|
|
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) =>
|
|
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(
|
|
3978
|
-
propertyTypeAnnotation[key].tsType = `(${
|
|
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 (
|
|
4206
|
+
const fetchDatasourceEntries = async (spaceId, datasourceId) => {
|
|
4167
4207
|
try {
|
|
4168
4208
|
const client = mapiClient();
|
|
4169
|
-
const { data } = await client.
|
|
4170
|
-
|
|
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 (
|
|
4223
|
+
const fetchDatasources = async (spaceId) => {
|
|
4176
4224
|
try {
|
|
4177
4225
|
const client = mapiClient();
|
|
4178
|
-
const { data } = await client.
|
|
4179
|
-
|
|
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
|
|
4182
|
-
|
|
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 (
|
|
4247
|
+
const fetchDatasource = async (spaceId, datasourceName) => {
|
|
4192
4248
|
try {
|
|
4193
4249
|
const client = mapiClient();
|
|
4194
|
-
const { data } = await client.
|
|
4195
|
-
|
|
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(
|
|
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:
|
|
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 (
|
|
4360
|
+
const pushDatasource = async (spaceId, datasource) => {
|
|
4295
4361
|
try {
|
|
4296
4362
|
const client = mapiClient();
|
|
4297
|
-
const { data } = await client.
|
|
4298
|
-
|
|
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 (
|
|
4375
|
+
const updateDatasource = async (spaceId, datasourceId, datasource) => {
|
|
4306
4376
|
try {
|
|
4307
4377
|
const client = mapiClient();
|
|
4308
|
-
const { data } = await client.
|
|
4309
|
-
|
|
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 (
|
|
4400
|
+
const pushDatasourceEntry = async (spaceId, datasourceId, entry) => {
|
|
4324
4401
|
try {
|
|
4325
4402
|
const client = mapiClient();
|
|
4326
|
-
const { data } = await client.
|
|
4327
|
-
|
|
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 (
|
|
4420
|
+
const updateDatasourceEntry = async (spaceId, entryId, entry) => {
|
|
4340
4421
|
try {
|
|
4341
4422
|
const client = mapiClient();
|
|
4342
|
-
await client.
|
|
4343
|
-
|
|
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:
|
|
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
|
-
|
|
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(
|
|
4625
|
+
async function deleteDatasource(spaceId, id) {
|
|
4539
4626
|
try {
|
|
4540
4627
|
const client = mapiClient();
|
|
4541
|
-
await client.delete(
|
|
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 ${
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
5023
|
+
const version = "4.5.0";
|
|
4936
5024
|
const pkg = {
|
|
4937
5025
|
version: version};
|
|
4938
5026
|
|