Skip to content

Commit 22a6800

Browse files
committed
docs(@merkur/plugin-error): expand Operation section, document renderContent, GenericError params getter, ExtensibleError, and error.url field
1 parent 8ddd97b commit 22a6800

1 file changed

Lines changed: 91 additions & 29 deletions

File tree

website/docs/error-plugin.md

Lines changed: 91 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,13 @@ description: Learn about the Error plugin for error handling in Merkur
66

77
# Error plugin
88

9-
A plugin for [Merkur](https://merkur.js.org/) is tiny extensible javascript library for front-end microservices.
9+
The error plugin adds semi-automatic error handling to your Merkur widget. It hooks into the widget lifecycle (`load`, `mount`, `update`) to catch thrown errors, saves the error state on the widget, and emits an event so the rest of the application can react.
1010

11-
12-
@merkur/plugin-error adds semi-automatic error handling to your Merkur widget:
13-
14-
* Return a custom HTTP status based on thrown error
15-
* Render valid JSON with error code and message
16-
* Render an error page
17-
* Run arbitrary code on error (e.g. to log/report the error)
18-
* Catch unhandled promise errors
19-
* Provide error class to extend
11+
- Returns a custom HTTP status based on the thrown error
12+
- Renders valid JSON with the error code and message via the `info` lifecycle
13+
- Renders an error page (re-runs `mount`/`update` after capturing the error)
14+
- Runs arbitrary code on error via the `ERROR_EVENTS.ERROR` event (e.g. logging)
15+
- Exposes `GenericError` and `ExtensibleError` base classes for custom errors
2016

2117

2218
## Installation
@@ -61,17 +57,28 @@ Set HTTP status in widget API response:
6157
6258
## Operation
6359
64-
When an error is thrown, the plugin does the following:
60+
The plugin hooks four lifecycle methods: `load`, `mount`, `update`, and `info`.
61+
62+
**`load` hook** — If `widget.error.status` is already set, `load` is skipped entirely and returns `{}`. Otherwise the original `load` is called; if it throws, the error is captured via `setErrorInfo` and an empty result is returned.
63+
64+
**`mount` / `update` hooks** — Delegate to `renderContent`:
65+
- If `widget.error.status` is already set when the hook runs, the original method is attempted once more (giving the view a chance to render its error state). If that also throws, an empty string is returned.
66+
- If no error is set and the original method throws, the error is captured and the hook immediately retries — this time the error state is set so the view can render accordingly.
67+
68+
**`info` hook** — Merges `widget.error` into the info object returned to the host application:
69+
70+
```javascript
71+
// info() output always includes the error field:
72+
{
73+
error: { status: null, message: null }, // or { status: 500, message: '...' }
74+
// ...other info fields
75+
}
76+
```
6577
66-
* saves the error status and message on the widget object:
67-
```javascript
68-
widget.error = {
69-
status: error.status || 500,
70-
message: error.message
71-
}
72-
```
73-
* [emit](/docs/event-emitter-plugin#emit) `ERROR` event with thrown error
74-
* Re-runs the function (if defined)
78+
When an error is captured, the plugin:
79+
1. Sets `widget.error.status`, `widget.error.message`, and `widget.error.url` (from `error.params?.url`)
80+
2. In development mode also sets `widget.error.stack`
81+
3. Emits `ERROR_EVENTS.ERROR` with the error object
7582
7683
The error object is available everywhere in the widget, as well as to the host application.
7784
@@ -102,21 +109,25 @@ Manually set error information on the widget. This function is used internally b
102109
103110
**Parameters:**
104111
- `widget` - The widget instance
105-
- `error` - Error object with `status` and `message` properties
112+
- `error` - Error object. Recognized properties:
113+
- `error.status` — HTTP status code (defaults to `500` if not set)
114+
- `error.message` — error message string
115+
- `error.params.url` — optional URL associated with the error, stored as `widget.error.url`
106116
107117
```javascript
108118
import { setErrorInfo } from '@merkur/plugin-error';
109119

110120
const customError = new Error('Custom error message');
111121
customError.status = 503;
122+
customError.params = { url: '/api/data' };
112123

113124
setErrorInfo(widget, customError);
114125

115126
console.log(widget.error);
116127
// {
117128
// status: 503,
118129
// message: 'Custom error message',
119-
// url: undefined
130+
// url: '/api/data'
120131
// }
121132
```
122133
@@ -128,28 +139,79 @@ console.log(widget.error);
128139
// {
129140
// status: 503,
130141
// message: 'Custom error message',
131-
// url: undefined,
142+
// url: '/api/data',
132143
// stack: '...' // Full stack trace
133144
// }
134145
```
135146
147+
### `renderContent(widget, method, properties)`
148+
149+
The core rendering helper used internally by the `mount` and `update` hooks. It can also be used directly when you need the same error-resilient render logic in a custom lifecycle method.
150+
151+
**Parameters:**
152+
- `widget` - The widget instance
153+
- `method` - An async function to call (e.g. the original `mount`/`update`)
154+
- `properties` - Array of arguments to pass to `method`
155+
156+
**Behaviour:**
157+
- If `widget.error.status` is already set: calls `method` once and returns its result, or returns `''` if it throws (without overwriting the existing error).
158+
- If `widget.error.status` is not set: calls `method`; if it throws, captures the error with `setErrorInfo` and recursively retries (now with error state set).
159+
160+
```javascript
161+
import { renderContent } from '@merkur/plugin-error';
162+
163+
// In a custom lifecycle hook:
164+
async function customRender(widget, originalRender, ...args) {
165+
return renderContent(widget, originalRender, args);
166+
}
167+
```
168+
136169
## Limitations
137170
138171
The plugin can't handle errors occurring outside of lifecycle functions.
139172
140173
## Custom errors
141174
142-
You can throw custom errors by instantiating `GenericError` class or create custom error classes that extend `GenericError`.
175+
### `GenericError`
176+
177+
`GenericError` extends `ExtensibleError` and is the recommended way to throw errors with a specific HTTP status code and additional parameters.
178+
179+
**Constructor:** `new GenericError(message, params)`
180+
- `message` — error message string
181+
- `params` — object with any extra data; `params.status` sets the HTTP status (defaults to `500`) and is removed from `params` itself
143182
144-
`GenericError` class carries `status` param among with other params. This way error plugin can respond with adequate HTTP status and also include data for encountered error.
183+
**`params` getter** — returns the extra parameters (excluding `status`):
145184
146185
```javascript
147186
import { GenericError } from '@merkur/plugin-error';
148187

149-
throw new GenericError('Operation failed.', {
150-
status: 500,
151-
reason: 'api_error'
152-
})
188+
const error = new GenericError('Operation failed.', {
189+
status: 503,
190+
reason: 'api_error',
191+
url: '/api/data',
192+
});
193+
194+
console.log(error.status); // 503
195+
console.log(error.message); // 'Operation failed.'
196+
console.log(error.params); // { reason: 'api_error', url: '/api/data' }
197+
console.log(error.params.url); // '/api/data' — stored as widget.error.url by setErrorInfo
198+
```
199+
200+
### `ExtensibleError`
201+
202+
`ExtensibleError` is the abstract base class that fixes Babel-related issues with extending the native `Error` class. Use it when you need a custom error hierarchy beyond `GenericError`:
203+
204+
```javascript
205+
import { GenericError } from '@merkur/plugin-error';
206+
// GenericError already extends ExtensibleError — extend it for custom errors:
207+
208+
class NotFoundError extends GenericError {
209+
constructor(url) {
210+
super(`Not found: ${url}`, { status: 404, url });
211+
}
212+
}
213+
214+
throw new NotFoundError('/missing-page');
153215
```
154216
155217
## Server-side Express Middleware

0 commit comments

Comments
 (0)