@visulima/ono 1.0.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/CHANGELOG.md +5 -0
- package/LICENSE.md +27 -0
- package/README.md +617 -0
- package/dist/error-inspector/page/create-request-context.d.ts +45 -0
- package/dist/error-inspector/page/create-request-context.js +163 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +1327 -0
- package/dist/packem_shared/clipboard-XQlKTYnW.js +1 -0
- package/dist/packem_shared/process-B37didfB.js +1 -0
- package/dist/packem_shared/types-B8vYgwmD.d.ts +53 -0
- package/dist/server/open-in-editor.d.ts +18 -0
- package/dist/server/open-in-editor.js +3 -0
- package/package.json +181 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
## @visulima/ono 1.0.0 (2025-09-12)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **ono:** initialize flare package with error handling utilities a… ([#515](https://github.com/visulima/visulima/issues/515)) ([9eaf418](https://github.com/visulima/visulima/commit/9eaf41878717a4d34c07f5513a60ca3e09bceda6))
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 visulima
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
<!-- DEPENDENCIES -->
|
|
24
|
+
<!-- /DEPENDENCIES -->
|
|
25
|
+
|
|
26
|
+
<!-- TYPE_DEPENDENCIES -->
|
|
27
|
+
<!-- /TYPE_DEPENDENCIES -->
|
package/README.md
ADDED
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h3>Visulima ono (Oh No!)</h3>
|
|
3
|
+
<p>
|
|
4
|
+
A modern, delightful error overlay and inspector for Node.js servers and dev tooling.
|
|
5
|
+
</p>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<br />
|
|
9
|
+
|
|
10
|
+
<div align="center">
|
|
11
|
+
|
|
12
|
+
[![typescript-image]][typescript-url] [![npm-image]][npm-url] [![license-image]][license-url]
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<div align="center">
|
|
19
|
+
<p>
|
|
20
|
+
<sup>
|
|
21
|
+
Daniel Bannert's open source work is supported by the community on <a href="https://github.com/sponsors/prisis">GitHub Sponsors</a>
|
|
22
|
+
</sup>
|
|
23
|
+
</p>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
| Light Mode | Dark Mode |
|
|
29
|
+
| -------------------------------- | ------------------------------ |
|
|
30
|
+
|  |  |
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
pnpm add @visulima/ono
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
npm i @visulima/ono
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
yarn add @visulima/ono
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
- Pretty, theme‑aware error page
|
|
49
|
+
- Sticky header (shows error name/message while scrolling)
|
|
50
|
+
- One‑click copy for error title (icon feedback)
|
|
51
|
+
- Stack trace viewer
|
|
52
|
+
- Shiki‑powered syntax highlighting (singleton highlighter)
|
|
53
|
+
- Tabs for frames; grouping for internal/node_modules/application frames
|
|
54
|
+
- Tooltips and labels to guide usage
|
|
55
|
+
- Optional "Open in editor" button per frame
|
|
56
|
+
- Error causes viewer (nested causes, each with its own viewer)
|
|
57
|
+
- Solutions panel
|
|
58
|
+
- Default open; smooth expand/collapse without layout jump
|
|
59
|
+
- Animated height/opacity; icon toggles open/close
|
|
60
|
+
- Built-in rule-based Markdown hints for common issues (ESM/CJS interop, export mismatch, port in use, missing files/case, TS path mapping, DNS/connection, React hydration mismatch, undefined property access)
|
|
61
|
+
- Custom solution finders support
|
|
62
|
+
- Raw stack trace panel
|
|
63
|
+
- Theme toggle (auto/dark/light) with persistence
|
|
64
|
+
- **Copy to Clipboard** - One-click copying for all data sections
|
|
65
|
+
- **Responsive Design** - Sticky sidebar navigation with smooth scrolling
|
|
66
|
+
- Consistent tooltips (one global script; components only output HTML)
|
|
67
|
+
|
|
68
|
+
**New in latest version:**
|
|
69
|
+
|
|
70
|
+
- **Tabbed Interface** - Switch between Stack and Context views
|
|
71
|
+
- **Request Context Panel** - Detailed HTTP request debugging information
|
|
72
|
+
- cURL command with proper formatting and copy functionality
|
|
73
|
+
- Headers, cookies, body, and session data
|
|
74
|
+
- App routing, client info, Git status, and version details
|
|
75
|
+
- Smart data sanitization and masking for sensitive information
|
|
76
|
+
- **Flexible Context API** - Add any custom context data via `createRequestContextPage()`
|
|
77
|
+
- **Modern Ono Class API** - Simple, consistent interface for both HTML and ANSI rendering
|
|
78
|
+
- **Solution Finders** - Extensible system for custom error solutions
|
|
79
|
+
|
|
80
|
+
Accessibility and keyboard UX
|
|
81
|
+
|
|
82
|
+
- ARIA-correct tabs and panels for stack frames; improved labeling
|
|
83
|
+
- Focus trap within the overlay; restores focus on close
|
|
84
|
+
- Keyboard shortcuts help dialog (press Shift+/ or “?” button)
|
|
85
|
+
- Buttons/controls are keyboard-activatable (Enter/Space)
|
|
86
|
+
|
|
87
|
+
Editor integration
|
|
88
|
+
|
|
89
|
+
- Editor selector is always visible; selection persists (localStorage)
|
|
90
|
+
- Uses server endpoint when configured; otherwise opens via editor URL scheme (defaults to VS Code)
|
|
91
|
+
|
|
92
|
+
### Using the Ono class (recommended)
|
|
93
|
+
|
|
94
|
+
The new Ono class provides a simple, consistent API for both HTML and ANSI error rendering:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { Ono } from "@visulima/ono";
|
|
98
|
+
|
|
99
|
+
const ono = new Ono();
|
|
100
|
+
|
|
101
|
+
// HTML error page
|
|
102
|
+
const html = await ono.toHTML(error, {
|
|
103
|
+
cspNonce: "your-nonce",
|
|
104
|
+
theme: "dark",
|
|
105
|
+
solutionFinders: [
|
|
106
|
+
/* custom finders */
|
|
107
|
+
],
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// ANSI terminal output
|
|
111
|
+
const { errorAnsi, solutionBox } = await ono.toANSI(error, {
|
|
112
|
+
solutionFinders: [
|
|
113
|
+
/* custom finders */
|
|
114
|
+
],
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Node.js HTTP Server Example
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { createServer } from "node:http";
|
|
122
|
+
import { Ono } from "@visulima/ono";
|
|
123
|
+
import { createRequestContextPage } from "@visulima/ono/page/context";
|
|
124
|
+
import { createNodeHttpHandler } from "@visulima/ono/server/open-in-editor";
|
|
125
|
+
|
|
126
|
+
const ono = new Ono();
|
|
127
|
+
const openInEditorHandler = createNodeHttpHandler();
|
|
128
|
+
|
|
129
|
+
const server = createServer(async (request, response) => {
|
|
130
|
+
const url = new URL(request.url || "/", `http://localhost:3000`);
|
|
131
|
+
|
|
132
|
+
// Open-in-editor endpoint
|
|
133
|
+
if (url.pathname === "/__open-in-editor") {
|
|
134
|
+
return openInEditorHandler(request, response);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
// Your app logic here
|
|
139
|
+
throw new Error("Something went wrong!");
|
|
140
|
+
} catch (error) {
|
|
141
|
+
// Create context page with request information
|
|
142
|
+
const contextPage = await createRequestContextPage(request, {
|
|
143
|
+
context: {
|
|
144
|
+
request: {
|
|
145
|
+
method: request.method,
|
|
146
|
+
url: request.url,
|
|
147
|
+
headers: request.headers,
|
|
148
|
+
},
|
|
149
|
+
user: {
|
|
150
|
+
client: {
|
|
151
|
+
ip: request.socket?.remoteAddress,
|
|
152
|
+
userAgent: request.headers["user-agent"],
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Generate HTML error page
|
|
159
|
+
const html = await ono.toHTML(error, {
|
|
160
|
+
content: [contextPage],
|
|
161
|
+
openInEditorUrl: "__open-in-editor",
|
|
162
|
+
cspNonce: "nonce-" + Date.now(),
|
|
163
|
+
theme: "auto",
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
response.writeHead(500, {
|
|
167
|
+
"Content-Type": "text/html",
|
|
168
|
+
"Content-Length": Buffer.byteLength(html, "utf8"),
|
|
169
|
+
});
|
|
170
|
+
response.end(html);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
server.listen(3000);
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Hono Framework Example
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { serve } from "@hono/node-server";
|
|
181
|
+
import { Hono } from "hono";
|
|
182
|
+
import { Ono } from "@visulima/ono";
|
|
183
|
+
import { createRequestContextPage } from "@visulima/ono/page/context";
|
|
184
|
+
|
|
185
|
+
const app = new Hono();
|
|
186
|
+
const ono = new Ono();
|
|
187
|
+
|
|
188
|
+
app.get("/", (c) => c.text("OK"));
|
|
189
|
+
|
|
190
|
+
app.get("/error", () => {
|
|
191
|
+
throw new Error("Boom from Hono");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
app.onError(async (err, c) => {
|
|
195
|
+
const contextPage = await createRequestContextPage(c.req.raw, {
|
|
196
|
+
context: {
|
|
197
|
+
request: {
|
|
198
|
+
method: c.req.method,
|
|
199
|
+
url: c.req.url,
|
|
200
|
+
headers: Object.fromEntries(c.req.raw.headers.entries()),
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const html = await ono.toHTML(err, {
|
|
206
|
+
content: [contextPage],
|
|
207
|
+
cspNonce: "hono-nonce-" + Date.now(),
|
|
208
|
+
theme: "dark",
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
return c.html(html, 500);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
serve({ fetch: app.fetch, port: 3000 });
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## API
|
|
218
|
+
|
|
219
|
+
### Ono Class
|
|
220
|
+
|
|
221
|
+
The main API for rendering errors in both HTML and ANSI formats.
|
|
222
|
+
|
|
223
|
+
#### Constructor
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
const ono = new Ono();
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### Methods
|
|
230
|
+
|
|
231
|
+
##### `toHTML(error, options?)` => `Promise<string>`
|
|
232
|
+
|
|
233
|
+
Renders an error as an HTML page.
|
|
234
|
+
|
|
235
|
+
- **error**: `unknown` - The error to render
|
|
236
|
+
- **options**: `TemplateOptions` (optional)
|
|
237
|
+
- `content?: ContentPage[]` - Additional pages to display as tabs
|
|
238
|
+
- `cspNonce?: string` - CSP nonce for inline scripts/styles
|
|
239
|
+
- `editor?: Editors` - Default editor for "Open in editor" functionality
|
|
240
|
+
- `openInEditorUrl?: string` - Server endpoint for opening files in editor
|
|
241
|
+
- `solutionFinders?: SolutionFinder[]` - Custom solution finders
|
|
242
|
+
- `theme?: 'dark' | 'light' | 'auto'` - Theme preference
|
|
243
|
+
|
|
244
|
+
Returns the complete HTML string for the error page.
|
|
245
|
+
|
|
246
|
+
##### `toANSI(error, options?)` => `Promise<{ errorAnsi: string; solutionBox?: string }>`
|
|
247
|
+
|
|
248
|
+
Renders an error as ANSI terminal output.
|
|
249
|
+
|
|
250
|
+
- **error**: `unknown` - The error to render
|
|
251
|
+
- **options**: `CliOptions` (optional)
|
|
252
|
+
- `solutionFinders?: SolutionFinder[]` - Custom solution finders
|
|
253
|
+
- All other options from `@visulima/error` renderError options
|
|
254
|
+
|
|
255
|
+
Returns an object with `errorAnsi` (the formatted error) and optional `solutionBox` (suggested solutions).
|
|
256
|
+
|
|
257
|
+
### createRequestContextPage(request, options) => `Promise<ContentPage | undefined>`
|
|
258
|
+
|
|
259
|
+
Creates a context page with detailed request debugging information.
|
|
260
|
+
|
|
261
|
+
- **request**: `Request` - The HTTP request object
|
|
262
|
+
- **options**: `ContextContentOptions`
|
|
263
|
+
- `context?: Record<string, unknown>` - Additional context data
|
|
264
|
+
- `headerAllowlist?: string[]` - Headers to include (default: all)
|
|
265
|
+
- `headerDenylist?: string[]` - Headers to exclude
|
|
266
|
+
- `maskValue?: string` - Mask for sensitive values (default: "[masked]")
|
|
267
|
+
|
|
268
|
+
### createNodeHttpHandler(options) => `(req, res) => void`
|
|
269
|
+
|
|
270
|
+
Creates an HTTP handler for opening files in editors.
|
|
271
|
+
|
|
272
|
+
- **options**: `OpenInEditorOptions` (optional)
|
|
273
|
+
- `projectRoot?: string` - Project root directory
|
|
274
|
+
- `allowOutsideProject?: boolean` - Allow opening files outside project
|
|
275
|
+
|
|
276
|
+
Returns an Express/Node.js compatible request handler.
|
|
277
|
+
|
|
278
|
+
### CLI Example
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
import { Ono } from "@visulima/ono";
|
|
282
|
+
|
|
283
|
+
const ono = new Ono();
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
throw new Error("Something went wrong!");
|
|
287
|
+
} catch (error) {
|
|
288
|
+
// Basic ANSI output
|
|
289
|
+
const result = await ono.toANSI(error);
|
|
290
|
+
console.log(result.errorAnsi);
|
|
291
|
+
|
|
292
|
+
if (result.solutionBox) {
|
|
293
|
+
console.log("\n" + result.solutionBox);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// With custom solution finder
|
|
297
|
+
const resultWithCustom = await ono.toANSI(error, {
|
|
298
|
+
solutionFinders: [
|
|
299
|
+
{
|
|
300
|
+
name: "custom-finder",
|
|
301
|
+
priority: 100,
|
|
302
|
+
handle: async (err, context) => ({
|
|
303
|
+
header: "Custom Solution",
|
|
304
|
+
body: "Try checking your configuration.",
|
|
305
|
+
}),
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Request Context Panel
|
|
313
|
+
|
|
314
|
+
Use `createRequestContextPage()` to create a "Context" tab with comprehensive debugging information:
|
|
315
|
+
|
|
316
|
+
- **Request Overview** - cURL command with proper formatting and copy functionality
|
|
317
|
+
- **Headers** - HTTP headers with smart masking for sensitive data
|
|
318
|
+
- **Body** - Request body content with proper formatting
|
|
319
|
+
- **Session** - Session data in organized key-value tables
|
|
320
|
+
- **Cookies** - Cookie information in readable format
|
|
321
|
+
- **Dynamic Context Sections** - Any additional context keys you provide are automatically rendered as sections with:
|
|
322
|
+
- Proper titles (capitalized)
|
|
323
|
+
- Copy buttons for JSON data
|
|
324
|
+
- Organized key-value tables
|
|
325
|
+
- Sticky sidebar navigation
|
|
326
|
+
|
|
327
|
+
**Built-in sections** (when data is provided):
|
|
328
|
+
|
|
329
|
+
- `app` - Application routing details (route, params, query)
|
|
330
|
+
- `user` - Client information (IP, User-Agent, geo)
|
|
331
|
+
- `git` - Repository status (branch, commit, tag, dirty state)
|
|
332
|
+
- `versions` - Package versions and dependencies
|
|
333
|
+
|
|
334
|
+
**Custom sections** - Add any context data you want:
|
|
335
|
+
|
|
336
|
+
- `database` - Database connection info, queries, etc.
|
|
337
|
+
- `cache` - Cache status and keys
|
|
338
|
+
- `environment` - Environment variables
|
|
339
|
+
- `performance` - Performance metrics
|
|
340
|
+
- And more!
|
|
341
|
+
|
|
342
|
+
**Deep Object & Array Support** - The context panel intelligently renders:
|
|
343
|
+
|
|
344
|
+
- Nested objects with proper indentation and visual hierarchy
|
|
345
|
+
- Arrays with indexed items and collapsible structure
|
|
346
|
+
- Complex data types (strings, numbers, booleans, null, undefined)
|
|
347
|
+
- Performance-optimized rendering with depth limits (max 3 levels)
|
|
348
|
+
- Smart truncation for large datasets (shows first 10 items/keys)
|
|
349
|
+
|
|
350
|
+
All sections include copy buttons for easy data extraction and debugging.
|
|
351
|
+
|
|
352
|
+
### Custom Solution Finders
|
|
353
|
+
|
|
354
|
+
Create custom solution finders to provide specific guidance for your application's errors:
|
|
355
|
+
|
|
356
|
+
```ts
|
|
357
|
+
import { Ono } from "@visulima/ono";
|
|
358
|
+
|
|
359
|
+
const customFinder = {
|
|
360
|
+
name: "my-app-finder",
|
|
361
|
+
priority: 100, // Higher priority = checked first
|
|
362
|
+
handle: async (error, context) => {
|
|
363
|
+
if (error.message.includes("database connection")) {
|
|
364
|
+
return {
|
|
365
|
+
header: "Database Connection Issue",
|
|
366
|
+
body: "Check your database configuration and ensure the server is running.",
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (error.message.includes("authentication")) {
|
|
371
|
+
return {
|
|
372
|
+
header: "Authentication Error",
|
|
373
|
+
body: "Verify your API keys and authentication tokens are valid.",
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return undefined; // No solution found
|
|
378
|
+
},
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
const ono = new Ono();
|
|
382
|
+
const html = await ono.toHTML(error, {
|
|
383
|
+
solutionFinders: [customFinder],
|
|
384
|
+
});
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Copy to Clipboard
|
|
388
|
+
|
|
389
|
+
All data sections in the Request Context Panel include copy buttons that:
|
|
390
|
+
|
|
391
|
+
- Copy data in JSON format for easy debugging
|
|
392
|
+
- Provide visual feedback (button changes to "Copied!" with green styling)
|
|
393
|
+
- Support both modern `navigator.clipboard` API and fallback methods
|
|
394
|
+
- Work across all browsers and environments
|
|
395
|
+
|
|
396
|
+
### Adding custom pages/tabs via `options.content`
|
|
397
|
+
|
|
398
|
+
You can add any number of custom pages using the `content` option:
|
|
399
|
+
|
|
400
|
+
```ts
|
|
401
|
+
import { Ono } from "@visulima/ono";
|
|
402
|
+
import { createRequestContextPage } from "@visulima/ono/page/context";
|
|
403
|
+
|
|
404
|
+
const ono = new Ono();
|
|
405
|
+
|
|
406
|
+
// Create a context page with request information
|
|
407
|
+
const contextPage = await createRequestContextPage(request, {
|
|
408
|
+
context: {
|
|
409
|
+
request: request,
|
|
410
|
+
app: { routing: { route: "/api/users", params: {}, query: {} } },
|
|
411
|
+
user: { client: { ip: "127.0.0.1", userAgent: "Mozilla/5.0..." } },
|
|
412
|
+
database: { connection: "active", queries: ["SELECT * FROM users"] },
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
// Add custom pages
|
|
417
|
+
const customPages = [
|
|
418
|
+
contextPage, // Context page with request info
|
|
419
|
+
{
|
|
420
|
+
id: "performance",
|
|
421
|
+
name: "Performance",
|
|
422
|
+
code: {
|
|
423
|
+
html: "<div><h3>Performance Metrics</h3><p>Custom performance data here...</p></div>",
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
id: "debug",
|
|
428
|
+
name: "Debug Info",
|
|
429
|
+
code: {
|
|
430
|
+
html: "<div><h3>Debug Information</h3><pre>" + JSON.stringify(debugData, null, 2) + "</pre></div>",
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
];
|
|
434
|
+
|
|
435
|
+
const html = await ono.toHTML(error, {
|
|
436
|
+
content: customPages,
|
|
437
|
+
cspNonce: "your-nonce",
|
|
438
|
+
});
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
## Examples
|
|
442
|
+
|
|
443
|
+
The `examples/` directory contains working examples for different use cases:
|
|
444
|
+
|
|
445
|
+
### CLI Example (`examples/cli/`)
|
|
446
|
+
|
|
447
|
+
Demonstrates basic ANSI output and custom solution finders:
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
cd examples/cli
|
|
451
|
+
node index.js
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Node.js HTTP Server (`examples/node/`)
|
|
455
|
+
|
|
456
|
+
Complete HTTP server example with rich context pages:
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
cd examples/node
|
|
460
|
+
node index.js
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
Try these routes:
|
|
464
|
+
|
|
465
|
+
- `/error` - Basic error with context
|
|
466
|
+
- `/esm-cjs` - ESM/CJS interop error
|
|
467
|
+
- `/export-mismatch` - Export mismatch error
|
|
468
|
+
- `/custom-solution` - Custom solution finder demo
|
|
469
|
+
|
|
470
|
+
### Hono Framework (`examples/hono/`)
|
|
471
|
+
|
|
472
|
+
Hono framework integration example:
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
cd examples/hono
|
|
476
|
+
node index.js
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
Try these routes:
|
|
480
|
+
|
|
481
|
+
- `/error` - Basic error handling
|
|
482
|
+
- `/error-html` - HTML error page
|
|
483
|
+
- `/api/error-json` - JSON error response
|
|
484
|
+
|
|
485
|
+
### Server helpers
|
|
486
|
+
|
|
487
|
+
From `@visulima/ono/server/open-in-editor`:
|
|
488
|
+
|
|
489
|
+
- `openInEditor(request, options)` — core function (uses `open-editor` under the hood)
|
|
490
|
+
- `createNodeHttpHandler(options)` — returns `(req, res) => void` for Node http servers
|
|
491
|
+
- `createExpressHandler(options)` — returns `(req, res) => void` for Express/Connect
|
|
492
|
+
|
|
493
|
+
Options:
|
|
494
|
+
|
|
495
|
+
- `projectRoot?: string` — defaults to `process.cwd()`
|
|
496
|
+
- `allowOutsideProject?: boolean` — defaults to `false`
|
|
497
|
+
|
|
498
|
+
### Editor selector
|
|
499
|
+
|
|
500
|
+
- Always visible
|
|
501
|
+
- Persists user choice in `localStorage` (`ono:editor`)
|
|
502
|
+
- Used for both the server opener (sent as `editor` in the POST body) and the client-side fallback
|
|
503
|
+
|
|
504
|
+
### Client-side fallback editor links
|
|
505
|
+
|
|
506
|
+
- If `openInEditorUrl` is not set, clicking “Open in editor” uses editor URL schemes on the client. The default editor is VS Code. The selected editor in the header is respected.
|
|
507
|
+
- Supported editors and templates (placeholders: `%f` = file, `%l` = line, `%c` = column when supported):
|
|
508
|
+
- textmate: `txmt://open?url=file://%f&line=%l`
|
|
509
|
+
- macvim: `mvim://open?url=file://%f&line=%l`
|
|
510
|
+
- emacs: `emacs://open?url=file://%f&line=%l`
|
|
511
|
+
- sublime: `subl://open?url=file://%f&line=%l`
|
|
512
|
+
- phpstorm: `phpstorm://open?file=%f&line=%l`
|
|
513
|
+
- atom: `atom://core/open/file?filename=%f&line=%l`
|
|
514
|
+
- atom-beta: `atom-beta://core/open/file?filename=%f&line=%l`
|
|
515
|
+
- brackets: `brackets://open?url=file://%f&line=%l`
|
|
516
|
+
- clion: `clion://open?file=%f&line=%l`
|
|
517
|
+
- code (VS Code): `vscode://file/%f:%l:%c`
|
|
518
|
+
- code-insiders: `vscode-insiders://file/%f:%l:%c`
|
|
519
|
+
- codium (VSCodium): `vscodium://file/%f:%l:%c`
|
|
520
|
+
- cursor: `cursor://file/%f:%l:%c`
|
|
521
|
+
- emacs: `emacs://open?url=file://%f&line=%l`
|
|
522
|
+
- idea: `idea://open?file=%f&line=%l`
|
|
523
|
+
- intellij: `idea://open?file=%f&line=%l`
|
|
524
|
+
- macvim: `mvim://open?url=file://%f&line=%l`
|
|
525
|
+
- notepad++: `notepad-plus-plus://open?file=%f&line=%l`
|
|
526
|
+
- phpstorm: `phpstorm://open?file=%f&line=%l`
|
|
527
|
+
- pycharm: `pycharm://open?file=%f&line=%l`
|
|
528
|
+
- rider: `rider://open?file=%f&line=%l`
|
|
529
|
+
- rubymine: `rubymine://open?file=%f&line=%l`
|
|
530
|
+
- sublime: `subl://open?url=file://%f&line=%l`
|
|
531
|
+
- textmate: `txmt://open?url=file://%f&line=%l`
|
|
532
|
+
- vim: `vim://open?url=file://%f&line=%l`
|
|
533
|
+
- visualstudio: `visualstudio://open?file=%f&line=%l`
|
|
534
|
+
- vscode: `vscode://file/%f:%l:%c`
|
|
535
|
+
- vscodium: `vscodium://file/%f:%l:%c`
|
|
536
|
+
- webstorm: `webstorm://open?file=%f&line=%l`
|
|
537
|
+
- xcode: `xcode://open?file=%f&line=%l`
|
|
538
|
+
- zed: `zed://open?file=%f&line=%l&column=%c`
|
|
539
|
+
- android-studio: `idea://open?file=%f&line=%l`
|
|
540
|
+
|
|
541
|
+
### Keyboard Shortcuts
|
|
542
|
+
|
|
543
|
+
- **Shift+/ (or ?)** — Open shortcuts help dialog
|
|
544
|
+
- **Esc** — Close dialogs
|
|
545
|
+
- **Enter/Space** — Activate focused control (e.g., toggles, tabs)
|
|
546
|
+
|
|
547
|
+
### Tooltips
|
|
548
|
+
|
|
549
|
+
Components emit HTML with `data-tooltip-trigger`; a single script exported by the tooltip module is imported once by the layout (so there's no duplication).
|
|
550
|
+
|
|
551
|
+
### Extend the VisulimaError
|
|
552
|
+
|
|
553
|
+
```ts
|
|
554
|
+
import { VisulimaError } from "@visulima/error";
|
|
555
|
+
|
|
556
|
+
class MyError extends VisulimaError {
|
|
557
|
+
constructor(message: string) {
|
|
558
|
+
super({
|
|
559
|
+
name: "MyError",
|
|
560
|
+
message,
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
throw new MyError("My error message");
|
|
566
|
+
|
|
567
|
+
// or
|
|
568
|
+
|
|
569
|
+
const error = new MyError("My error message");
|
|
570
|
+
|
|
571
|
+
error.hint = "My error hint";
|
|
572
|
+
|
|
573
|
+
throw error;
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
### Pretty code frame
|
|
577
|
+
|
|
578
|
+
```ts
|
|
579
|
+
import { codeFrame } from "@visulima/error";
|
|
580
|
+
|
|
581
|
+
const source = "const x = 10;\nconst error = x.y;\n";
|
|
582
|
+
const loc = { column: 16, line: 2 };
|
|
583
|
+
|
|
584
|
+
const frame = codeFrame(source, loc);
|
|
585
|
+
|
|
586
|
+
console.log(frame);
|
|
587
|
+
// 1 | const x = 10;
|
|
588
|
+
// > 2 | const error = x.y;
|
|
589
|
+
// | ^
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
## Supported Node.js Versions
|
|
593
|
+
|
|
594
|
+
Libraries in this ecosystem make the best effort to track [Node.js’ release schedule](https://github.com/nodejs/release#release-schedule).
|
|
595
|
+
Here’s [a post on why we think this is important](https://medium.com/the-node-js-collection/maintainers-should-consider-following-node-js-release-schedule-ab08ed4de71a).
|
|
596
|
+
|
|
597
|
+
## Contributing
|
|
598
|
+
|
|
599
|
+
If you would like to help take a look at the [list of issues](https://github.com/visulima/visulima/issues) and check our [Contributing](.github/CONTRIBUTING.md) guild.
|
|
600
|
+
|
|
601
|
+
> **Note:** please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.
|
|
602
|
+
|
|
603
|
+
## Credits
|
|
604
|
+
|
|
605
|
+
- [Daniel Bannert](https://github.com/prisis)
|
|
606
|
+
- [All Contributors](https://github.com/visulima/visulima/graphs/contributors)
|
|
607
|
+
|
|
608
|
+
## License
|
|
609
|
+
|
|
610
|
+
The visulima error is open-sourced software licensed under the [MIT][license-url]
|
|
611
|
+
|
|
612
|
+
[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
|
|
613
|
+
[typescript-url]: "typescript"
|
|
614
|
+
[license-image]: https://img.shields.io/npm/l/@visulima/ono?color=blueviolet&style=for-the-badge
|
|
615
|
+
[license-url]: LICENSE.md "license"
|
|
616
|
+
[npm-image]: https://img.shields.io/npm/v/@visulima/ono/latest.svg?style=for-the-badge&logo=npm
|
|
617
|
+
[npm-url]: https://www.npmjs.com/package/@visulima/ono/v/latest "npm"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { C as ContentPage } from '../../packem_shared/types-B8vYgwmD.js';
|
|
2
|
+
import { IncomingMessage } from 'node:http';
|
|
3
|
+
|
|
4
|
+
type NativeRequest = typeof globalThis.Request;
|
|
5
|
+
type NativeHeaders = typeof globalThis.Headers;
|
|
6
|
+
interface CustomHeaders {
|
|
7
|
+
entries: () => IterableIterator<[string, string]>;
|
|
8
|
+
forEach: (callback: (value: string, key: string) => void) => void;
|
|
9
|
+
get: (name: string) => string | null;
|
|
10
|
+
}
|
|
11
|
+
interface CustomRequest {
|
|
12
|
+
clone?: () => CustomRequest;
|
|
13
|
+
headers?: Record<string, string | string[]> | CustomHeaders;
|
|
14
|
+
json?: () => Promise<unknown>;
|
|
15
|
+
method?: string;
|
|
16
|
+
text?: () => Promise<string>;
|
|
17
|
+
url?: string;
|
|
18
|
+
}
|
|
19
|
+
type HeadersLike = NativeHeaders | CustomHeaders;
|
|
20
|
+
type RequestLike = NativeRequest | (IncomingMessage & {
|
|
21
|
+
body?: unknown;
|
|
22
|
+
clone?: () => RequestLike;
|
|
23
|
+
json?: () => Promise<unknown>;
|
|
24
|
+
text?: () => Promise<string>;
|
|
25
|
+
}) | CustomRequest | {
|
|
26
|
+
body?: unknown;
|
|
27
|
+
headers?: Record<string, string | string[]> | HeadersLike;
|
|
28
|
+
method?: string;
|
|
29
|
+
off?: (event: string, handler: (chunk: unknown) => void) => void;
|
|
30
|
+
on?: (event: string, handler: (chunk: unknown) => void) => void;
|
|
31
|
+
setEncoding?: (encoding: string) => void;
|
|
32
|
+
url?: string;
|
|
33
|
+
};
|
|
34
|
+
type ContextContentOptions = {
|
|
35
|
+
context?: Record<string, unknown>;
|
|
36
|
+
headerAllowlist?: string[];
|
|
37
|
+
headerDenylist?: string[];
|
|
38
|
+
maskValue?: string;
|
|
39
|
+
previewBytes?: number;
|
|
40
|
+
totalCapBytes?: number;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
declare const createRequestContext: (request: RequestLike, options: ContextContentOptions) => Promise<ContentPage | undefined>;
|
|
44
|
+
|
|
45
|
+
export { createRequestContext as default };
|