storyblok 4.17.1 → 4.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -68,7 +68,7 @@ interface StoryblokMultilinkUrl {
68
68
  url: string;
69
69
  full_slug: string;
70
70
  }
71
- interface StoryblokMultilink {
71
+ interface StoryblokMultilinkBase {
72
72
  fieldtype: 'multilink';
73
73
  id: string;
74
74
  url: string;
@@ -78,10 +78,18 @@ interface StoryblokMultilink {
78
78
  rel?: string;
79
79
  title?: string;
80
80
  prep?: string;
81
- linktype: 'story' | 'url' | 'email' | 'asset';
82
- story?: StoryblokMultilinkStory | StoryblokMultilinkLink | StoryblokMultilinkUrl;
83
- email?: string;
84
81
  }
82
+ type StoryblokMultilink = (StoryblokMultilinkBase & {
83
+ linktype: 'story';
84
+ story?: StoryblokMultilinkStory | StoryblokMultilinkLink | StoryblokMultilinkUrl;
85
+ }) | (StoryblokMultilinkBase & {
86
+ linktype: 'url';
87
+ }) | (StoryblokMultilinkBase & {
88
+ linktype: 'email';
89
+ email: string;
90
+ }) | (StoryblokMultilinkBase & {
91
+ linktype: 'asset';
92
+ });
85
93
  interface StoryblokTable {
86
94
  fieldtype: 'table';
87
95
  thead: Array<{
package/dist/index.d.ts CHANGED
@@ -68,7 +68,7 @@ interface StoryblokMultilinkUrl {
68
68
  url: string;
69
69
  full_slug: string;
70
70
  }
71
- interface StoryblokMultilink {
71
+ interface StoryblokMultilinkBase {
72
72
  fieldtype: 'multilink';
73
73
  id: string;
74
74
  url: string;
@@ -78,10 +78,18 @@ interface StoryblokMultilink {
78
78
  rel?: string;
79
79
  title?: string;
80
80
  prep?: string;
81
- linktype: 'story' | 'url' | 'email' | 'asset';
82
- story?: StoryblokMultilinkStory | StoryblokMultilinkLink | StoryblokMultilinkUrl;
83
- email?: string;
84
81
  }
82
+ type StoryblokMultilink = (StoryblokMultilinkBase & {
83
+ linktype: 'story';
84
+ story?: StoryblokMultilinkStory | StoryblokMultilinkLink | StoryblokMultilinkUrl;
85
+ }) | (StoryblokMultilinkBase & {
86
+ linktype: 'url';
87
+ }) | (StoryblokMultilinkBase & {
88
+ linktype: 'email';
89
+ email: string;
90
+ }) | (StoryblokMultilinkBase & {
91
+ linktype: 'asset';
92
+ });
85
93
  interface StoryblokTable {
86
94
  fieldtype: 'table';
87
95
  thead: Array<{
package/dist/index.mjs CHANGED
@@ -578,10 +578,12 @@ function setActiveConfig(config) {
578
578
 
579
579
  class FetchError extends Error {
580
580
  response;
581
- constructor(message, response) {
581
+ request;
582
+ constructor(message, response, request = {}) {
582
583
  super(message);
583
584
  this.name = "FetchError";
584
585
  this.response = response;
586
+ this.request = request;
585
587
  }
586
588
  }
587
589
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -589,6 +591,7 @@ async function customFetch(url, options = {}) {
589
591
  const { api } = getActiveConfig();
590
592
  const maxRetries = options.maxRetries ?? api.maxRetries;
591
593
  const baseDelay = options.baseDelay ?? 500;
594
+ const requestContext = { url, method: options.method ?? "GET" };
592
595
  let attempt = 0;
593
596
  while (attempt <= maxRetries) {
594
597
  try {
@@ -612,7 +615,7 @@ async function customFetch(url, options = {}) {
612
615
  status: response.status,
613
616
  statusText: response.statusText,
614
617
  data: null
615
- });
618
+ }, requestContext);
616
619
  }
617
620
  if (!response.ok) {
618
621
  if (response.status === 429 && attempt < maxRetries) {
@@ -625,7 +628,7 @@ async function customFetch(url, options = {}) {
625
628
  status: response.status,
626
629
  statusText: response.statusText,
627
630
  data
628
- });
631
+ }, requestContext);
629
632
  }
630
633
  return {
631
634
  ...data,
@@ -640,14 +643,14 @@ async function customFetch(url, options = {}) {
640
643
  status: 0,
641
644
  statusText: "Network Error",
642
645
  data: null
643
- });
646
+ }, requestContext);
644
647
  }
645
648
  }
646
649
  throw new FetchError("Max retries exceeded", {
647
650
  status: 429,
648
651
  statusText: "Rate Limit Exceeded",
649
652
  data: null
650
- });
653
+ }, requestContext);
651
654
  }
652
655
 
653
656
  const API_ACTIONS = {
@@ -719,9 +722,14 @@ function handleAPIError(action, error, customMessage) {
719
722
  }
720
723
  const response = error?.response;
721
724
  if (response?.status) {
725
+ const reqCandidate = error?.request;
722
726
  const wrappedError = new FetchError(
723
727
  response.statusText ?? error.message,
724
- { status: response.status, statusText: response.statusText ?? "", data: response.data }
728
+ { status: response.status, statusText: response.statusText ?? "", data: response.data },
729
+ {
730
+ url: typeof reqCandidate?.url === "string" ? reqCandidate.url : void 0,
731
+ method: typeof reqCandidate?.method === "string" ? reqCandidate.method : void 0
732
+ }
725
733
  );
726
734
  const errorId = getErrorId(response.status);
727
735
  throw new APIError(errorId, action, wrappedError, customMessage);
@@ -763,6 +771,8 @@ class APIError extends Error {
763
771
  }
764
772
  }
765
773
  getInfo() {
774
+ const request = this.error?.request;
775
+ const hasRequestContext = Boolean(request && (request.url || request.method));
766
776
  return {
767
777
  name: this.name,
768
778
  message: this.message,
@@ -770,7 +780,8 @@ class APIError extends Error {
770
780
  cause: this.cause,
771
781
  errorId: this.errorId,
772
782
  stack: this.stack,
773
- responseData: this.response?.data
783
+ responseData: this.response?.data,
784
+ ...hasRequestContext ? { request: { url: request.url, method: request.method } } : {}
774
785
  };
775
786
  }
776
787
  }
@@ -2268,16 +2279,40 @@ const DEFAULT_GROUPS_FILENAME = "groups";
2268
2279
  const DEFAULT_PRESETS_FILENAME = "presets";
2269
2280
  const DEFAULT_TAGS_FILENAME = "tags";
2270
2281
 
2282
+ async function fetchAllPages(fetchFunction, extractDataFunction) {
2283
+ const items = [];
2284
+ let page = 1;
2285
+ while (true) {
2286
+ const { data, response } = await fetchFunction(page);
2287
+ const totalHeader = response.headers.get("total");
2288
+ const fetchedItems = extractDataFunction(data);
2289
+ items.push(...fetchedItems);
2290
+ if (!totalHeader) {
2291
+ return items;
2292
+ }
2293
+ const total = Number(totalHeader);
2294
+ if (Number.isNaN(total) || items.length >= total || fetchedItems.length === 0) {
2295
+ return items;
2296
+ }
2297
+ page++;
2298
+ }
2299
+ }
2300
+
2271
2301
  const fetchComponents = async (spaceId) => {
2272
2302
  try {
2273
2303
  const client = getMapiClient();
2274
- const { data } = await client.components.list({
2275
- path: {
2276
- space_id: Number(spaceId)
2277
- },
2278
- throwOnError: true
2279
- });
2280
- return data?.components;
2304
+ return await fetchAllPages(
2305
+ (page) => client.components.list({
2306
+ path: {
2307
+ space_id: Number(spaceId)
2308
+ },
2309
+ query: {
2310
+ page
2311
+ },
2312
+ throwOnError: true
2313
+ }),
2314
+ (data) => data?.components ?? []
2315
+ );
2281
2316
  } catch (error) {
2282
2317
  handleAPIError("pull_components", error);
2283
2318
  }
@@ -2285,16 +2320,20 @@ const fetchComponents = async (spaceId) => {
2285
2320
  const fetchComponent = async (spaceId, componentName) => {
2286
2321
  try {
2287
2322
  const client = getMapiClient();
2288
- const { data } = await client.components.list({
2289
- path: {
2290
- space_id: Number(spaceId)
2291
- },
2292
- query: {
2293
- search: componentName
2294
- },
2295
- throwOnError: true
2296
- });
2297
- return data?.components?.find((c) => c.name === componentName);
2323
+ const matches = await fetchAllPages(
2324
+ (page) => client.components.list({
2325
+ path: {
2326
+ space_id: Number(spaceId)
2327
+ },
2328
+ query: {
2329
+ page,
2330
+ search: componentName
2331
+ },
2332
+ throwOnError: true
2333
+ }),
2334
+ (data) => data?.components ?? []
2335
+ );
2336
+ return matches.find((c) => c.name === componentName);
2298
2337
  } catch (error) {
2299
2338
  handleAPIError("pull_components", error, `Failed to fetch component ${componentName}`);
2300
2339
  }
@@ -2328,12 +2367,19 @@ const fetchComponentPresets = async (spaceId) => {
2328
2367
  const fetchComponentInternalTags = async (spaceId) => {
2329
2368
  try {
2330
2369
  const client = getMapiClient();
2331
- const { data } = await client.internalTags.list({
2332
- path: {
2333
- space_id: Number(spaceId)
2334
- }
2335
- });
2336
- return data?.internal_tags?.filter((tag) => tag.object_type === "component");
2370
+ return await fetchAllPages(
2371
+ (page) => client.internalTags.list({
2372
+ path: {
2373
+ space_id: Number(spaceId)
2374
+ },
2375
+ query: {
2376
+ page,
2377
+ by_object_type: "component"
2378
+ },
2379
+ throwOnError: true
2380
+ }),
2381
+ (data) => data?.internal_tags ?? []
2382
+ );
2337
2383
  } catch (error) {
2338
2384
  handleAPIError("pull_component_internal_tags", error);
2339
2385
  }
@@ -6114,20 +6160,6 @@ generateCmd.action(async (options, command) => {
6114
6160
  const program$6 = getProgram();
6115
6161
  const datasourcesCommand = program$6.command(commands.DATASOURCES).alias("ds").description(`Manage your space's datasources`);
6116
6162
 
6117
- async function fetchAllPages(fetchFunction, extractDataFunction, page = 1, collectedItems = []) {
6118
- const { data, response } = await fetchFunction(page);
6119
- const totalHeader = response.headers.get("total");
6120
- const total = Number(totalHeader);
6121
- const fetchedItems = extractDataFunction(data);
6122
- const allItems = [...collectedItems, ...fetchedItems];
6123
- if (!totalHeader || Number.isNaN(total)) {
6124
- return allItems;
6125
- }
6126
- if (allItems.length < total && fetchedItems.length > 0) {
6127
- return fetchAllPages(fetchFunction, extractDataFunction, page + 1, allItems);
6128
- }
6129
- return allItems;
6130
- }
6131
6163
  const fetchDatasourceEntries = async (spaceId, datasourceId) => {
6132
6164
  try {
6133
6165
  const client = getMapiClient();
@@ -6180,16 +6212,20 @@ const fetchDatasources = async (spaceId) => {
6180
6212
  const fetchDatasource = async (spaceId, datasourceName) => {
6181
6213
  try {
6182
6214
  const client = getMapiClient();
6183
- const { data } = await client.datasources.list({
6184
- path: {
6185
- space_id: Number(spaceId)
6186
- },
6187
- query: {
6188
- search: datasourceName
6189
- },
6190
- throwOnError: true
6191
- });
6192
- const found = data.datasources?.find((d) => d.name === datasourceName);
6215
+ const matches = await fetchAllPages(
6216
+ (page) => client.datasources.list({
6217
+ path: {
6218
+ space_id: Number(spaceId)
6219
+ },
6220
+ query: {
6221
+ page,
6222
+ search: datasourceName
6223
+ },
6224
+ throwOnError: true
6225
+ }),
6226
+ (data) => data.datasources || []
6227
+ );
6228
+ const found = matches.find((d) => d.name === datasourceName);
6193
6229
  if (!found) {
6194
6230
  return void 0;
6195
6231
  }
@@ -8057,13 +8093,6 @@ const fetchStoryStream = ({
8057
8093
  }
8058
8094
  });
8059
8095
  };
8060
- const getUUIDFromFilename = (filename) => {
8061
- const uuid = basename(filename, extname(filename)).split("_").at(-1);
8062
- if (!uuid) {
8063
- throw new Error(`Unable to extract UUID from local story "${filename}"`);
8064
- }
8065
- return uuid;
8066
- };
8067
8096
  const readLocalStoriesStream = ({
8068
8097
  directoryPath,
8069
8098
  fileFilter = () => true,
@@ -8073,7 +8102,7 @@ const readLocalStoriesStream = ({
8073
8102
  onStoryError
8074
8103
  }) => {
8075
8104
  const listGenerator = async function* localStoryIterator() {
8076
- const files = (await readDirectory(directoryPath)).filter((f) => extname(f) === ".json" && fileFilter({ uuid: getUUIDFromFilename(f) }));
8105
+ const files = (await readDirectory(directoryPath)).filter((f) => extname(f) === ".json" && fileFilter({ filename: f }));
8077
8106
  setTotalStories?.(files.length);
8078
8107
  for (const file of files) {
8079
8108
  try {
@@ -8141,10 +8170,13 @@ const scanLocalStoryIndex = async ({
8141
8170
  const filePath = join(directoryPath, file);
8142
8171
  const fileContent = await readFile$1(filePath, "utf-8");
8143
8172
  const story = JSON.parse(fileContent);
8173
+ if (!story.uuid) {
8174
+ throw new Error(`Story "${file}" is missing a uuid and cannot be pushed.`);
8175
+ }
8144
8176
  entries.push({
8145
8177
  filename: file,
8146
8178
  id: story.id,
8147
- uuid: story.uuid ?? "",
8179
+ uuid: story.uuid,
8148
8180
  slug: story.slug ?? "",
8149
8181
  name: story.name ?? "",
8150
8182
  full_slug: story.full_slug ?? "",
@@ -9248,12 +9280,14 @@ pushCmd.action(async (options, command) => {
9248
9280
  pendingWarnings.push(message);
9249
9281
  logger.warn(message);
9250
9282
  }
9283
+ const uuidByFilename = new Map(storyIndex.map((entry) => [entry.filename, entry.uuid]));
9251
9284
  await pipeline$1(
9252
9285
  // Read local stories from `.json` files.
9253
9286
  readLocalStoriesStream({
9254
9287
  directoryPath: storiesDirectoryPath,
9255
- fileFilter({ uuid }) {
9256
- return Boolean(maps.stories.get(uuid));
9288
+ fileFilter({ filename }) {
9289
+ const uuid = uuidByFilename.get(filename);
9290
+ return uuid != null && Boolean(maps.stories.get(uuid));
9257
9291
  },
9258
9292
  setTotalStories(total) {
9259
9293
  summary.processResults.total = total;