@andersundsehr/storybook-typo3 0.1.11 → 0.1.13

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.
@@ -6,7 +6,7 @@ export async function fetchComponent(component) {
6
6
  alert(message);
7
7
  throw new Error(message);
8
8
  }
9
- const data = await fetchWithUserRetry(url + '/_storybook/componentMeta?viewHelper=' + component, {}, 'metadata for component `' + component + '` from TYPO3');
9
+ const data = await fetchWithUserRetry(url + '/_storybook/componentMeta?viewHelper=' + component, {}, 'metadata for component `' + component + '`');
10
10
  return {
11
11
  fullName: component,
12
12
  name: component.split(':')[1],
@@ -32,7 +32,7 @@ export async function fetchPreviewConfig(currentGlobals) {
32
32
  'Content-Type': 'application/json',
33
33
  },
34
34
  body: JSON.stringify({ globals }),
35
- }, 'preview config from TYPO3');
35
+ }, 'preview config');
36
36
  }
37
37
  export function initGlobalsHandling(initalGlobalTypes) {
38
38
  const channel = addons.getChannel();
@@ -15,6 +15,6 @@ export async function fetchRenderAction(urlA, id, params, storyContext) {
15
15
  return await fetchWithUserRetry(url + '/_storybook/render', {
16
16
  method: 'POST',
17
17
  body: JSON.stringify(body),
18
- }, 'rendering component in TYPO3', 'text');
18
+ }, 'rendering component', 'text');
19
19
  }
20
20
  ;
@@ -1 +1,3 @@
1
- export declare function fetchWithUserRetry<T>(url: string, options: RequestInit, message: string, result?: 'json' | 'text'): Promise<T>;
1
+ type PossibleResults = string | object;
2
+ export declare function fetchWithUserRetry<T extends PossibleResults>(url: string, options: RequestInit, message: string, resultType?: 'json' | 'text'): Promise<T>;
3
+ export {};
@@ -1,11 +1,11 @@
1
1
  /*
2
2
  We want to make it obvious for the user what he has to do.
3
3
 
4
- We have 2 different usecases:
4
+ We have 2 different use cases:
5
5
  - background fetches (componentMeta & preview)
6
6
  - html Fetches
7
7
 
8
- most of the time the user has to do something in there IDE to fix the problem.
8
+ most of the time the user has to do something in their IDE to fix the problem.
9
9
  So we want to show a message that tells the user what he has to do.
10
10
 
11
11
  In the case of a background fetch, we want to show the full error in the console.
@@ -13,40 +13,144 @@ And give the user a alert with the error message and a retry button.
13
13
 
14
14
  In the case of a html fetch, we want to show the error as html response?
15
15
  - yes: because the user can see the error in the browser and can fix it
16
- - no: because storybook dose not understand that it is an error
16
+ - no: because storybook does not understand that it is an error
17
17
 
18
18
  In any case we should get the error in json so we can handle it in the frontend as we like.
19
- Some times it can still be text/html so we need to handle that as well.
19
+ Sometimes it can still be text/html so we need to handle that as well.
20
20
  */
21
- export async function fetchWithUserRetry(url, options, message, result = 'json') {
22
- options = { ...options }; // Clone options to avoid mutating the original
23
- options.signal = options.signal || AbortSignal.timeout(5000);
21
+ let updatableApiKey = import.meta.env.STORYBOOK_TYPO3_KEY;
22
+ export async function fetchWithUserRetry(url, options, message, resultType = 'json') {
24
23
  try {
24
+ options = { ...options }; // Clone options to avoid mutating the original
25
+ options.signal = options.signal || AbortSignal.timeout(5000);
26
+ options.headers = {
27
+ ...options.headers,
28
+ 'Content-Type': 'application/json',
29
+ Accept: resultType === 'json' ? 'application/json' : 'text/html',
30
+ 'X-Storybook-TYPO3-Key': updatableApiKey,
31
+ };
25
32
  const response = await fetch(url, options);
26
33
  if (!response.ok) {
27
- // let responseBody = await response.text();
28
- // if (responseBody) {
29
- // responseBody += '\n';
30
- // }
31
- // throw new Error(`${responseBody}HTTP ${response.status}\nURL: ${url}\nError while fetching ${message}`);
34
+ return retry(response, url, options, message, resultType);
32
35
  }
33
- if (result === 'text') {
34
- return await response.text();
35
- }
36
- return await response.json();
36
+ return (await response[resultType]());
37
37
  }
38
38
  catch (error) {
39
- console.error('Fetch failed:', { error });
40
- const retry = confirm(''
41
- + 'ERROR: ' + String(error) + '\n\n\n'
42
- + 'Error while fetching ' + message + '. \n\n'
43
- + 'fetch: ' + url + '\n\n'
44
- + 'look at the console for more details.\n\n'
45
- + 'Do you want to retry?');
46
- if (retry) {
47
- options.signal = undefined; // Reset the signal to avoid reusing the same AbortSignal
48
- return fetchWithUserRetry(url, options, message);
49
- }
50
- throw error; // Re-throw the error if the user does not want to retry
39
+ return retry(error, url, options, message, resultType);
40
+ }
41
+ }
42
+ async function retry(errorOrResponse, url, options, message, resultType) {
43
+ const errorType = await getErrorExplanation(errorOrResponse);
44
+ let exception = errorOrResponse instanceof Error ? errorOrResponse : undefined;
45
+ let retry = false;
46
+ let confirmationMessage = '';
47
+ if (errorType.errorType === 'key') {
48
+ retry = true;
49
+ confirmationMessage = `šŸ” ${errorType.message}\n\n`
50
+ + `šŸ”— url: ${url}\n\n`
51
+ + `šŸ’¬ Please insert the correct API key:\ncan be found in the .env file:\n\n`;
52
+ // if there was no api key, it will be set into the .env from the php code automatically
53
+ // we wait 1 second to give HMR time to update the .env file and reload the page, otherwise the user has to provide the key
54
+ if (!updatableApiKey) {
55
+ await new Promise(resolve => setTimeout(resolve, 1000));
56
+ }
57
+ updatableApiKey = prompt(confirmationMessage) || updatableApiKey;
58
+ }
59
+ else if (errorType.errorType === 'network') {
60
+ confirmationMessage = `šŸ›œ+🚫 A network error occurred while fetching ${message} from TYPO3:\n\n`
61
+ + `šŸ’¬ ${errorType.message}\n\n`
62
+ + `ā© Please check your network connection and try again.\n\n`
63
+ + `šŸ”— url: ${url}\n\n`;
64
+ exception = new Error(`Network error: ${errorType.message}`);
65
+ }
66
+ else if (errorType.errorType === 'extension') {
67
+ if (resultType === 'text') {
68
+ // no retry via confirm()
69
+ return errorType.errorHtml;
70
+ }
71
+ confirmationMessage = `šŸ’„ An error occurred while fetching ${message} from TYPO3:\n\n`
72
+ + `šŸ’¬ ${errorType.reason}\n\n`
73
+ + `${errorType.stackTrace ? `šŸ•µšŸ»ā€ā™‚ļø Stack trace: ${errorType.stackTrace}\n\n` : ''}`
74
+ + `šŸ”— url: ${url}\n\n`;
75
+ exception = new Error(`Extension error: ${errorType.reason}`);
76
+ }
77
+ else {
78
+ if (resultType === 'text' && errorType.message) {
79
+ // no retry via confirm()
80
+ return errorType.message;
81
+ }
82
+ // unknown error
83
+ confirmationMessage = `😳 An error occurred while fetching ${message} from TYPO3:\n\n`
84
+ + `šŸ’¬ ${errorType.message}\n\n`
85
+ + `#ļøāƒ£ Status code: ${errorType.statusCode || 'unknown'}\n\n`
86
+ + `šŸ”— url: ${url}\n\n`;
87
+ }
88
+ confirmationMessage = confirmationMessage.trim();
89
+ confirmationMessage = confirmationMessage.length > 700 ? confirmationMessage.substring(0, 700 - 3) + '\n…' : confirmationMessage;
90
+ if (!retry) {
91
+ retry = confirm(confirmationMessage + '\n\n Do you want to retry šŸ”„ā“');
92
+ }
93
+ if (retry) {
94
+ options.signal = undefined;
95
+ return fetchWithUserRetry(url, options, message, resultType);
96
+ }
97
+ throw exception || new Error(`Failed to fetch ${message} from TYPO3: ${JSON.stringify(errorType)}`);
98
+ }
99
+ async function getErrorExplanation(errorOrResponse) {
100
+ if (errorOrResponse instanceof Response) {
101
+ let text = undefined;
102
+ try {
103
+ text = await errorOrResponse.text();
104
+ }
105
+ catch (e) {
106
+ }
107
+ if (errorOrResponse.status === 401) {
108
+ return {
109
+ errorType: 'key',
110
+ message: text || `The API key is not set or invalid. Maybe you only need to restart Storybook?`,
111
+ };
112
+ }
113
+ if (!text) {
114
+ return {
115
+ errorType: 'unknown',
116
+ statusCode: errorOrResponse.status,
117
+ message: `Failed to read response from TYPO3: ${errorOrResponse.status}\n ${errorOrResponse.statusText}`,
118
+ };
119
+ }
120
+ try {
121
+ const extensionError = JSON.parse(text);
122
+ if (extensionError && typeof extensionError === 'object' && 'errorType' in extensionError && extensionError.errorType === 'extension') {
123
+ return extensionError;
124
+ }
125
+ return {
126
+ errorType: 'unknown',
127
+ statusCode: errorOrResponse.status,
128
+ message: `Received an unexpected response from TYPO3: ${errorOrResponse.status}\n ${JSON.stringify(extensionError)}`,
129
+ };
130
+ }
131
+ catch (e) {
132
+ console.warn('ā‰ļø Failed to parse JSON from TYPO3 response:', e);
133
+ }
134
+ return {
135
+ errorType: 'unknown',
136
+ statusCode: errorOrResponse.status,
137
+ message: `Received an unexpected response from TYPO3: ${errorOrResponse.status}\n ${text}`,
138
+ };
139
+ }
140
+ if (errorOrResponse instanceof Error) {
141
+ return {
142
+ errorType: 'network',
143
+ message: errorOrResponse.message,
144
+ };
145
+ }
146
+ if (typeof errorOrResponse === 'string') {
147
+ return {
148
+ errorType: 'unknown',
149
+ message: errorOrResponse,
150
+ };
51
151
  }
152
+ return {
153
+ errorType: 'unknown',
154
+ message: `An unknown error occurred: ${JSON.stringify(errorOrResponse) || ''}`,
155
+ };
52
156
  }
package/package.json CHANGED
@@ -49,5 +49,5 @@
49
49
  "dist",
50
50
  "README.md"
51
51
  ],
52
- "version": "0.1.11"
52
+ "version": "0.1.13"
53
53
  }