nostics 0.0.0 → 0.0.4

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.
@@ -0,0 +1,297 @@
1
+ ---
2
+ name: nostics
3
+ description: "Structured diagnostic code library for JavaScript/TypeScript. Turns errors, warnings, suggestions, and deprecations into typed, machine-readable Diagnostic objects with stable codes, docs URLs, and actionable fields. Use this skill whenever the project imports `@anthropic/nostics`, `nostics`, or works with `defineDiagnostics`, `createLogger`, `CodedError`, diagnostic code registries, structured error handling, or error code documentation pages. Also use when building custom formatters, reporters, or integrating diagnostic codes into a library or framework."
4
+ ---
5
+
6
+ # nostics
7
+
8
+ Structured diagnostic code library for JavaScript/TypeScript. Every error, warning, suggestion, and deprecation becomes a typed, serializable `Diagnostic` object with a stable code, docs URL, and actionable fields.
9
+
10
+ ## Core Concepts
11
+
12
+ ### Object-first design
13
+
14
+ The fundamental unit is a plain `Diagnostic` object — serializable, transportable (client-server, worker-main, build tool-IDE). An `Error` class (`CodedError`) is only created at the moment `.throw()` is called.
15
+
16
+ ```ts
17
+ interface Diagnostic {
18
+ code: string // e.g. 'NUXT_B2011'
19
+ level: 'error' | 'warn' | 'suggestion' | 'deprecation'
20
+ message: string // human-readable, already interpolated
21
+ why?: string // why this happened
22
+ fix?: string // how to resolve it
23
+ hint?: string // additional guidance
24
+ docs?: string // URL to documentation page for this code
25
+ sources?: SourceLocation[]
26
+ cause?: unknown
27
+ context?: Record<string, unknown>
28
+ }
29
+ ```
30
+
31
+ ### Diagnostic levels
32
+
33
+ - `error` — something is broken and must be fixed
34
+ - `warn` — something may cause issues
35
+ - `suggestion` — an improvement the user could make
36
+ - `deprecation` — something that will be removed in a future version
37
+
38
+ ## API Reference
39
+
40
+ ### `defineDiagnostics(options)` — Define diagnostic codes
41
+
42
+ Creates typed factory functions that produce plain `Diagnostic` objects. No side effects, no logging.
43
+
44
+ ```ts
45
+ import { defineDiagnostics } from 'nostics'
46
+
47
+ const diagnostics = defineDiagnostics({
48
+ docsBase: code => `https://nuxt.com/e/${code.replace('NUXT_', '').toLowerCase()}`,
49
+ codes: {
50
+ NUXT_B1001: {
51
+ message: 'Could not compile template.',
52
+ fix: 'Check the template for syntax errors.',
53
+ },
54
+ NUXT_B2011: {
55
+ message: (p: { src: string }) => `Invalid plugin \`${p.src}\`. src option is required.`,
56
+ fix: 'Pass a string path or an object with a `src` property to `addPlugin()`.',
57
+ },
58
+ NUXT_B5001: {
59
+ message: 'Missing compatibilityDate in nuxt.config.',
60
+ fix: (p: { date: string }) => `Add \`compatibilityDate: '${p.date}'\` to your nuxt.config.`,
61
+ hint: 'This ensures consistent behavior across Nuxt versions.',
62
+ level: 'warn',
63
+ },
64
+ },
65
+ })
66
+ ```
67
+
68
+ **Options:**
69
+
70
+ | Field | Type | Description |
71
+ |-------|------|-------------|
72
+ | `docsBase` | `string \| ((code: string) => string \| undefined)?` | Docs URL source. As a string, auto-generates `docs` as `${docsBase}/${code.toLowerCase()}`. As a function, receives the code key and returns the full URL (or `undefined` to omit). |
73
+ | `codes` | `Record<string, DiagnosticDefinition>` | Map of code keys to their definitions |
74
+
75
+ **DiagnosticDefinition fields:**
76
+
77
+ | Field | Type | Description |
78
+ |-------|------|-------------|
79
+ | `message` | `string \| (params) => string` | Required. The diagnostic message |
80
+ | `fix` | `string \| (params) => string` | Optional. How to resolve the issue |
81
+ | `why` | `string \| (params) => string` | Optional. Why this diagnostic was triggered |
82
+ | `hint` | `string \| (params) => string` | Optional. Additional contextual guidance |
83
+ | `level` | `DiagnosticLevel` | Optional. Defaults to `'error'` |
84
+
85
+ **Type inference:** Parameters are extracted from ALL template fields (`message`, `fix`, `why`, `hint`) and intersected. If `message` needs `{ src }` and `fix` needs `{ date }`, the factory requires `{ src, date }`.
86
+
87
+ **Factory usage:**
88
+
89
+ ```ts
90
+ // No params — factory takes optional overrides only
91
+ diagnostics.NUXT_B1001()
92
+ diagnostics.NUXT_B1001({ level: 'warn' })
93
+
94
+ // With params — first arg is params, second is optional overrides
95
+ diagnostics.NUXT_B2011({ src: '/plugins/bad.ts' })
96
+ diagnostics.NUXT_B2011({ src: '/plugins/bad.ts' }, { cause: originalError })
97
+ ```
98
+
99
+ **Registry methods** (non-enumerable on the result):
100
+
101
+ ```ts
102
+ diagnostics.codes() // → ['NUXT_B1001', 'NUXT_B2011', 'NUXT_B5001']
103
+ diagnostics.has('NUXT_B1001') // → true (type guard: narrows to known code key)
104
+ diagnostics.get('NUXT_B1001') // → the raw DiagnosticDefinition
105
+ diagnostics.extend({ // → new diagnostics set with additional codes merged in
106
+ NUXT_B9999: { message: 'New code.' }
107
+ })
108
+ ```
109
+
110
+ ### `createLogger(options)` — Bind diagnostics to output
111
+
112
+ Merges diagnostic sets and wraps each code factory to return `DiagnosticActions` — a `Diagnostic` enriched with action methods.
113
+
114
+ ```ts
115
+ import { consoleReporter, createLogger, plainFormatter } from 'nostics'
116
+ import { ansiFormatter } from 'nostics/formatters/ansi'
117
+
118
+ const log = createLogger({
119
+ diagnostics: [diagnostics],
120
+ formatter: ansiFormatter(colors), // or plainFormatter (default)
121
+ reporter: consoleReporter, // default — pass a single function or an array
122
+ })
123
+ ```
124
+
125
+ **Usage:**
126
+
127
+ ```ts
128
+ log.NUXT_B2011({ src: pluginPath }).throw() // formats, reports, then throws CodedError
129
+ log.NUXT_B1001().warn() // overrides level to 'warn', formats, reports
130
+ log.NUXT_B5001({ date: '2025-01-01' }).log() // uses the definition's level ('warn')
131
+ log.NUXT_B1001().format() // returns the formatted string only
132
+
133
+ // Raw methods for pre-built Diagnostic objects
134
+ log.throw(diagnostics.NUXT_B2011({ src: pluginPath }))
135
+ log.warn(diagnostics.NUXT_B1001())
136
+ ```
137
+
138
+ **Multiple diagnostic sets:**
139
+
140
+ ```ts
141
+ const log = createLogger({
142
+ diagnostics: [nuxtDiagnostics, i18nDiagnostics],
143
+ })
144
+
145
+ log.NUXT_B2011({ src: pluginPath }).throw() // [NUXT_B2011] ...
146
+ log.I18N_I001({ locale: 'fr' }).warn() // [I18N_I001] ...
147
+ ```
148
+
149
+ ### `CodedError` — Error class
150
+
151
+ Created only when `.throw()` is called. Extends `Error` with `name: 'CodedError'`.
152
+
153
+ ```ts
154
+ class CodedError extends Error {
155
+ readonly diagnostic: Diagnostic // the full object
156
+ }
157
+ ```
158
+
159
+ The `message` is formatted as `[CODE] message text`. Access all diagnostic fields via `err.diagnostic`.
160
+
161
+ **Catch and inspect:**
162
+
163
+ ```ts
164
+ try {
165
+ log.NUXT_B2011({ src: pluginPath }).throw()
166
+ }
167
+ catch (err) {
168
+ if (err instanceof CodedError) {
169
+ console.log(err.diagnostic.code) // 'NUXT_B2011'
170
+ console.log(err.diagnostic.docs) // 'https://nuxt.com/e/b2011'
171
+ console.log(err.diagnostic.fix) // 'Pass a string path...'
172
+ }
173
+ }
174
+ ```
175
+
176
+ ### Formatters
177
+
178
+ All are plain functions: `(d: Diagnostic) => string`.
179
+
180
+ | Formatter | Import | Description |
181
+ |-----------|--------|-------------|
182
+ | `plainFormatter` | `nostics` | Unicode box-drawing, no colors. Default. |
183
+ | `ansiFormatter(colors)` | `nostics/formatters/ansi` | Accepts a generic `Colors` interface — no hard ANSI dependency |
184
+ | `jsonFormatter` | `nostics/formatters/json` | `JSON.stringify(diagnostic)` |
185
+
186
+ **ANSI formatter `Colors` interface:**
187
+
188
+ ```ts
189
+ interface Colors {
190
+ red: (s: string) => string
191
+ yellow: (s: string) => string
192
+ cyan: (s: string) => string
193
+ gray: (s: string) => string
194
+ bold: (s: string) => string
195
+ dim: (s: string) => string
196
+ }
197
+ ```
198
+
199
+ **Plain formatter output:**
200
+
201
+ ```
202
+ [NUXT_B2011] Invalid plugin `/plugins/bad.ts`. src option is required.
203
+ ├▶ why: The plugin object was passed without a src path
204
+ ├▶ fix: Pass a string path or an object with a `src` property to `addPlugin()`.
205
+ ├▶ hint: Check your module's addPlugin() calls
206
+ ╰▶ see: https://nuxt.com/e/b2011
207
+ ```
208
+
209
+ Detail line order is fixed: `why` → `fix` → `hint` → `see` (docs URL). Missing fields are omitted.
210
+
211
+ **Writing a custom formatter:**
212
+
213
+ ```ts
214
+ import type { Formatter } from 'nostics'
215
+
216
+ const myFormatter: Formatter = (d) => {
217
+ return `[${d.code}] ${d.message}${d.fix ? ` (fix: ${d.fix})` : ''}`
218
+ }
219
+ ```
220
+
221
+ ### Formatting utilities
222
+
223
+ Two lower-level functions are exported for building custom formatters:
224
+
225
+ - `formatTag(d: Diagnostic)` — returns the `[CODE]` tag string (e.g. `[NUXT_B2011]` for code `'NUXT_B2011'`)
226
+ - `renderFrame(d: Diagnostic)` — returns the full box-drawing formatted string (same as `plainFormatter`)
227
+
228
+ ### Reporters
229
+
230
+ All are plain functions: `(d: Diagnostic, formatted: string) => void`. Pass a single reporter or an array.
231
+
232
+ | Reporter | Import | Description |
233
+ |----------|--------|-------------|
234
+ | `consoleReporter` | `nostics` | `console.error` for `'error'` level, `console.warn` for all others |
235
+ | `createFetchReporter(url)` | `nostics` | POSTs diagnostic JSON to the given URL (silently ignores fetch errors) |
236
+
237
+ **Writing a custom reporter:**
238
+
239
+ ```ts
240
+ import type { Reporter } from 'nostics'
241
+
242
+ const fileReporter: Reporter = (diagnostic, formatted) => {
243
+ fs.appendFileSync('errors.log', `${formatted}\n`)
244
+ }
245
+ ```
246
+
247
+ ### Overrides
248
+
249
+ When calling a factory, you can pass `Overrides` to attach runtime context:
250
+
251
+ ```ts
252
+ type Overrides = Partial<Pick<Diagnostic, 'level' | 'sources' | 'cause' | 'context'>>
253
+ ```
254
+
255
+ ```ts
256
+ diagnostics.NUXT_B2011({ src: pluginPath }, {
257
+ cause: originalError,
258
+ sources: [{ file: 'nuxt.config.ts', line: 42 }],
259
+ context: { moduleName: 'my-module' },
260
+ })
261
+ ```
262
+
263
+ ## Best Practices
264
+
265
+ ### Code naming conventions
266
+
267
+ - Use fully qualified, stable code identifiers (e.g. `NUXT_B1001`, `I18N_I001`)
268
+ - Group by domain using a letter prefix within the code: `B` for build, `R` for runtime, `C` for config, etc.
269
+ - Never reuse or reassign a code once published — codes are permanent identifiers
270
+
271
+ ### Structuring diagnostic definitions
272
+
273
+ - Always provide `message` — it is the only required field
274
+ - Provide `fix` whenever the solution is known — this is the most actionable field for both humans and AI agents
275
+ - Provide `why` to explain root causes when the reason may not be obvious from the message alone
276
+ - Use `hint` for supplementary guidance that doesn't fit in `fix` (e.g. links to related docs, edge cases)
277
+ - Set the appropriate `level` — default is `'error'`, override to `'warn'`, `'suggestion'`, or `'deprecation'` as needed
278
+ - Use parameterized templates for messages that include runtime values — avoid string concatenation outside the factory
279
+
280
+ ### Organizing diagnostic files
281
+
282
+ For large projects, split diagnostics by domain:
283
+
284
+ ```
285
+ src/
286
+ diagnostics/
287
+ build.ts # NUXT_B-series codes
288
+ runtime.ts # NUXT_R-series codes
289
+ config.ts # NUXT_C-series codes
290
+ index.ts # re-exports and merges all sets
291
+ ```
292
+
293
+ Each file calls `defineDiagnostics()` with the same `docsBase` but different code ranges.
294
+
295
+ ## Documentation Site
296
+
297
+ For guidance on building error code documentation pages (structure, templates, deployment, and AI-agent optimization), read `references/documentation-site.md`.
@@ -0,0 +1,203 @@
1
+ # Documentation Site and Error Code Registry
2
+
3
+ ## Table of Contents
4
+
5
+ - [Why a documentation site matters](#why-a-documentation-site-matters)
6
+ - [Setting up docsBase](#setting-up-docsbase)
7
+ - [Documentation page structure](#documentation-page-structure)
8
+ - [Page template](#page-template)
9
+ - [Deployment recommendations](#deployment-recommendations)
10
+ - [Keeping docs in sync with code](#keeping-docs-in-sync-with-code)
11
+ - [Optimizing for AI agent consumption](#optimizing-for-ai-agent-consumption)
12
+
13
+ ## Why a documentation site matters
14
+
15
+ Every diagnostic code should have a dedicated, publicly accessible documentation page. This serves three audiences:
16
+
17
+ 1. **Developers** encountering the error in their terminal or logs can click the `see:` URL and get immediate guidance
18
+ 2. **AI agents** (Claude, Copilot, etc.) can fetch the page content to provide contextual help when a user pastes an error
19
+ 3. **Search engines** index these pages so developers searching for `NUXT_B2011` find the answer directly
20
+
21
+ ## Setting up docsBase
22
+
23
+ The `docsBase` option in `defineDiagnostics()` controls the auto-generated `docs` URL. It can be a string or a function:
24
+
25
+ ```ts
26
+ // Function form — full control over the URL
27
+ const diagnostics = defineDiagnostics({
28
+ docsBase: code => `https://nuxt.com/e/${code.replace('NUXT_', '').toLowerCase()}`,
29
+ codes: {
30
+ NUXT_B2011: { message: '...' },
31
+ },
32
+ })
33
+ // diagnostics.NUXT_B2011().docs → 'https://nuxt.com/e/b2011'
34
+ ```
35
+
36
+ ```ts
37
+ // String form — code appended automatically as ${docsBase}/${code.toLowerCase()}
38
+ const diagnostics = defineDiagnostics({
39
+ docsBase: 'https://example.com/errors',
40
+ codes: {
41
+ MY_E001: { message: '...' },
42
+ },
43
+ })
44
+ // diagnostics.MY_E001().docs → 'https://example.com/errors/my_e001'
45
+ ```
46
+
47
+ Plan your URL structure accordingly.
48
+
49
+ ## Documentation page structure
50
+
51
+ Each error code page (e.g. `https://nuxt.com/e/b2011`) should follow this structure. The content must be both human-readable and optimized for AI agent consumption — use clear headings, concise language, and structured sections.
52
+
53
+ ### Required sections
54
+
55
+ **Title and code identifier**
56
+
57
+ ```markdown
58
+ # NUXT_B2011: Invalid plugin — src option is required
59
+
60
+ Code: `NUXT_B2011`
61
+ Level: error
62
+ ```
63
+
64
+ Start with the code and a short human-readable title. Include the code and the severity level.
65
+
66
+ **What this error means**
67
+
68
+ ```markdown
69
+ ## What this error means
70
+
71
+ This error occurs when a plugin is registered via `addPlugin()` without providing a
72
+ valid `src` path. Nuxt requires every plugin to have a source file so it can be
73
+ resolved and included in the build.
74
+ ```
75
+
76
+ Explain the error in plain language. Assume the reader has no prior context — describe what the system expected vs what it received. This section is the primary content AI agents will use to explain the error to users.
77
+
78
+ **Why this happens**
79
+
80
+ ```markdown
81
+ ## Why this happens
82
+
83
+ Common causes:
84
+
85
+ - Passing an object to `addPlugin()` without a `src` property
86
+ - Passing `undefined` or `null` as the plugin path
87
+ - A module is constructing a plugin object dynamically and the `src` field is missing
88
+ due to a conditional branch or typo
89
+ ```
90
+
91
+ List the concrete scenarios that trigger this diagnostic. Use bullet points. Each bullet should be a specific, recognizable situation the developer might be in.
92
+
93
+ **How to fix it**
94
+
95
+ ```markdown
96
+ ## How to fix it
97
+
98
+ Ensure every call to `addPlugin()` includes a valid `src` path:
99
+
100
+ \```ts
101
+ // Wrong
102
+ addPlugin({ name: 'my-plugin' })
103
+
104
+ // Correct
105
+ addPlugin({ src: resolve('./runtime/my-plugin'), name: 'my-plugin' })
106
+
107
+ // Also correct — pass a string directly
108
+ addPlugin(resolve('./runtime/my-plugin'))
109
+ \```
110
+
111
+ If the plugin path is computed dynamically, verify the variable is defined before
112
+ passing it to `addPlugin()`.
113
+ ```
114
+
115
+ Provide concrete code examples showing the wrong pattern and the corrected version. This is the most important section — it should be copy-pasteable.
116
+
117
+ ### Optional sections
118
+
119
+ **Additional context**
120
+
121
+ ```markdown
122
+ ## Additional context
123
+
124
+ - This validation was added in Nuxt 3.2
125
+ - If you are writing a Nuxt module, see the [Module Author Guide](https://nuxt.com/docs/guide/going-further/modules)
126
+ - Related codes: [NUXT_B1001](./nuxt_b1001) (template compilation), [NUXT_B3005](./nuxt_b3005) (module resolution)
127
+ ```
128
+
129
+ Link to related documentation, changelog entries, or related diagnostic codes.
130
+
131
+ **Example diagnostic output**
132
+
133
+ ```markdown
134
+ ## Example output
135
+
136
+ \```
137
+ [NUXT_B2011] Invalid plugin `/plugins/bad.ts`. src option is required.
138
+ ├▶ why: The plugin object was passed without a src path
139
+ ├▶ fix: Pass a string path or an object with a `src` property to `addPlugin()`.
140
+ ├▶ hint: Check your module's addPlugin() calls
141
+ ╰▶ see: https://nuxt.com/e/b2011
142
+ \```
143
+ ```
144
+
145
+ Show what the user actually sees in their terminal so they can confirm they're on the right page.
146
+
147
+ ## Page template
148
+
149
+ Use this template for each error code page:
150
+
151
+ ```markdown
152
+ # {CODE}: {Short title}
153
+
154
+ Code: `{CODE}`
155
+ Level: {error|warn|suggestion|deprecation}
156
+
157
+ ## What this error means
158
+
159
+ {Plain-language explanation of the diagnostic. 1-3 sentences.}
160
+
161
+ ## Why this happens
162
+
163
+ {Bulleted list of concrete scenarios that trigger this diagnostic.}
164
+
165
+ ## How to fix it
166
+
167
+ {Code examples showing the wrong pattern and the corrected version.}
168
+
169
+ ## Additional context
170
+
171
+ {Links to related docs, changelog, or related diagnostic codes. Optional.}
172
+
173
+ ## Example output
174
+
175
+ {Terminal output showing the formatted diagnostic. Optional.}
176
+ ```
177
+
178
+ ## Deployment recommendations
179
+
180
+ Host the error code pages on a public URL that matches your `docsBase`:
181
+
182
+ - **GitHub Pages or static site generator** (VitePress, Nuxt Content, etc.) — create a directory of markdown files, one per code, with a catch-all route at `/e/[code].md`
183
+ - **Dedicated `/errors` or `/e` route** in your existing documentation site
184
+ - Ensure pages return proper HTTP status codes (200 for valid codes, 404 for unknown codes) so agents and crawlers can distinguish valid codes from missing ones
185
+ - Use `<meta>` tags or frontmatter for structured data (code, level, title) to improve agent and search engine consumption
186
+ - Keep pages lightweight — avoid heavy JavaScript or SPAs that block content rendering for fetch-based agents
187
+
188
+ ## Keeping docs in sync with code
189
+
190
+ - Store documentation markdown alongside your diagnostic definitions or in a dedicated `docs/errors/` directory
191
+ - Generate an index page listing all codes with their messages and levels
192
+ - In CI, validate that every code in `defineDiagnostics()` has a corresponding documentation page — fail the build if a page is missing
193
+ - When adding a new diagnostic code, add the documentation page in the same PR
194
+
195
+ ## Optimizing for AI agent consumption
196
+
197
+ Pages should be structured so that an AI agent fetching the URL can extract the relevant information without ambiguity:
198
+
199
+ - Use consistent heading hierarchy (`##` for sections)
200
+ - Put the most actionable content (fix instructions) early
201
+ - Avoid hiding critical information in collapsed sections, tabs, or JavaScript-rendered content
202
+ - Include the code in the page title and body so keyword matching works
203
+ - Keep code examples self-contained — an agent should be able to suggest the fix from the page content alone
package/index.js DELETED
@@ -1 +0,0 @@
1
- // Placeholder