🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

nostics

Package Overview
Dependencies
Maintainers
2
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nostics - npm Package Compare versions

Comparing version
0.5.0
to
1.0.0
+284
dist/diagnostic-ChOW5Avm.d.mts
//#region src/utils.d.ts
/**
* A value of type T, or a function that resolves T from a single params object.
*
* @internal
*/
type ValueOrFn<T, P = any> = T | ((params: P) => T);
/**
* Extracts the param type from a single-arg function, or `never` for
* non-function inputs. Pairs with {@link ValueOrFn}.
*
* @internal
*/
type ExtractFnParam<T> = T extends ((params: infer P) => any) ? P : never;
/**
* Converts a union of types to their intersection.
*
* @internal
*/
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
/**
* `true` when `T` is the `any` type.
*
* @internal
*/
type IsAny<Type> = 0 extends 1 & Type ? true : false;
/**
* `true` when `T` is the `unknown` type (and not `any`).
*
* @internal
*/
type IsUnknown<Type> = IsAny<Type> extends true ? false : unknown extends Type ? true : false;
/**
* Expands a type to its property listing so editor hovers show the resolved
* shape instead of a chain of aliases / intersections.
*
* @internal
*/
type Prettify<Type> = { [Key in keyof Type]: Type[Key] };
//#endregion
//#region src/diagnostic.d.ts
/**
* Define-time shape of a diagnostic. Each field can be a static value or a
* function that resolves it from a shared `params` object passed at call
* time. Runtime-only fields (`cause`, `sources`) from {@link DiagnosticInit}
* are intentionally omitted: they're only meaningful at the call site.
*/
interface DiagnosticDefinition<P = any> {
/**
* The error message: why this failed. String, or a function of `params`.
*
* @example
* ```ts
* why: (p: { name: string }) => `module "${p.name}" failed to load`
* ```
*/
why: ValueOrFn<string, P>;
/**
* Actionable instructions on how to resolve the problem. String, or a
* function of `params`.
*
* @example
* ```ts
* fix: (p: { name: string }) => `run "npm install ${p.name}"`
* ```
*/
fix?: ValueOrFn<string, P>;
/**
* Per-code docs URL. A string overrides
* {@link DefineDiagnosticsOptions.docsBase} for this code; `false` opts this
* code out entirely, even when `docsBase` is set. When omitted, the URL is
* derived from `docsBase`.
*/
docs?: string | false;
}
/**
* Runtime-only fields that can be passed alongside the interpolation params
* at call time. Merged into the same object so callers pass everything in
* one place.
*/
interface DiagnosticCallParams {
/**
* Original error or exception that triggered this diagnostic. Pass it
* through when re-throwing so the original stack trace is preserved.
*/
cause?: unknown;
/**
* Locations in user code that contributed to this diagnostic, in
* `file:line:column` format. Useful for compilers and other tools where the
* JS stack trace doesn't reflect the user's source.
*/
sources?: string[];
}
/**
* Structured initializer for a {@link Diagnostic}. `why` is the only required
* field: it becomes the {@link Diagnostic.message}. The remaining fields are
* optional metadata that reporters and consumers can render or forward.
*/
interface DiagnosticInit extends DiagnosticCallParams {
/**
* The actual error message: why this failed.
* Mirrored to `Error.message`.
*/
why: string;
/**
* Optional actionable instructions on how to resolve the problem.
*/
fix?: string;
/**
* URL to extended documentation for this diagnostic.
*/
docs?: string;
}
/**
* Represents how to report a diagnostic. Could call `console.log()`, send the
* diagnostic to a server, or something else. Reporters declare the shape of
* options they need via `ReporterOpts`; `defineDiagnostics` intersects every
* reporter's options into a single object passed at the call site.
*/
type DiagnosticReporter<ReporterOpts extends object = {}> = (diagnostic: Diagnostic, options: ReporterOpts) => void;
/**
* Permissive reporter constraint used internally so reporters with 1 arg,
* required options, or optional options all satisfy the array constraint.
*
* @internal
*/
type AnyDiagnosticReporter = (diagnostic: Diagnostic, options: any) => void;
/**
* The `console` methods a log reporter can route to.
*/
type ConsoleMethod = "log" | "error" | "warn";
/**
* Options for {@link createConsoleReporter}.
*/
interface ConsoleReporterOptions {
/**
* `console` method used to print the diagnostic. Defaults to `'warn'`. The
* returned reporter still accepts a per-call `{ method }` override through
* the call-site reporter options.
*/
method?: ConsoleMethod;
/**
* Renders the diagnostic into the string handed to `console`. Defaults to
* {@link formatDiagnostic}, the plain unicode-decorated formatter.
*/
formatter?: (diagnostic: Diagnostic) => string;
}
/**
* Creates a console reporter that renders each diagnostic with `formatter` and
* prints the result via `console[method]`. Both default sensibly (`'warn'` and
* {@link formatDiagnostic}); `method` can also be overridden per call through
* the reporter options.
*/
declare function createConsoleReporter({
method: defaultMethod,
formatter
}?: ConsoleReporterOptions): DiagnosticReporter<{
method?: ConsoleMethod;
}>;
/**
* Resolves the `params` type a code expects from the intersection of params
* across all function-typed fields, falling back to `{}` when every field is
* static. Merged with {@link DiagnosticCallParams} at the call site.
*
* @internal
*/
type InferCodeParams<Def> = [ExtractFnParam<Def[keyof Def]>] extends [never] ? {} : UnionToIntersection<ExtractFnParam<Def[keyof Def]>>;
/**
* Options for {@link defineDiagnostics}.
*/
interface DefineDiagnosticsOptions<Codes extends Record<string, DiagnosticDefinition>, Reporters extends readonly AnyDiagnosticReporter[]> {
/**
* Base URL or resolver for documentation links. When a string, the code is
* appended as a lowercase path segment (e.g. `"https://docs.example.com"` →
* `"https://docs.example.com/math_e001"`). When a function, receives the
* code and returns a URL or `undefined`.
*/
docsBase?: string | ((code: keyof Codes) => string | undefined);
/**
* Map of diagnostic codes to their definitions.
*/
codes: Codes;
/**
* Reporters called every time a diagnostic is produced. Can be used to
* integrate with custom logging.
*/
reporters?: Reporters;
}
/**
* The first positional argument of a {@link DiagnosticHandle} call:
* interpolation params merged with the runtime-only call-site fields
* (`cause`, `sources`).
*
* @internal
*/
type CallSiteParams<Params> = Params & DiagnosticCallParams;
/**
* Resolves the full argument tuple for a {@link DiagnosticHandle} call.
* Branches on whether params and reporter options each have required fields.
* Required positions become required tuple elements, all-optional ones
* become `?`, and when no reporter declares any options the parameter is
* omitted entirely.
*
* @internal
*/
type ActionArgs<Params, ReporterOpts> = keyof ReporterOpts extends never ? {} extends Params ? [params?: CallSiteParams<Params>] : [params: CallSiteParams<Params>] : {} extends ReporterOpts ? {} extends Params ? [params?: CallSiteParams<Params>, reporterOptions?: ReporterOpts] : [params: CallSiteParams<Params>, reporterOptions?: ReporterOpts] : {} extends Params ? [params: CallSiteParams<Params> | undefined, reporterOptions: ReporterOpts] : [params: CallSiteParams<Params>, reporterOptions: ReporterOpts];
/**
* Per-code handle exposed by {@link defineDiagnostics}. Each code is a
* callable: invoke it to build the diagnostic and run every reporter, or
* prefix the call with `throw` to raise it.
*
* @example
* ```ts
* diagnostics.MATH_E001({ name: 'x' }) // report
* throw diagnostics.MATH_E001({ name: 'x' }) // throw
* ```
*/
interface DiagnosticHandle<Params, ReporterOpts> {
/**
* Builds the diagnostic, runs every reporter, and returns the diagnostic
* instance. The returned diagnostic can be inspected, attached as `cause`,
* or thrown with `throw`.
*/
(...args: ActionArgs<Params, ReporterOpts>): Diagnostic;
}
/**
* Return type of {@link defineDiagnostics}.
*/
type Diagnostics<Codes extends Record<string, DiagnosticDefinition>, Reporters extends readonly AnyDiagnosticReporter[]> = { [Code in keyof Codes]: DiagnosticHandle<InferCodeParams<Codes[Code]>, Prettify<ExtractReportersOptions<Reporters>>> };
declare class Diagnostic extends Error {
name: string;
/**
* URL to extended documentation for this diagnostic code.
* Auto-generated from {@link DefineDiagnosticsOptions.docsBase}.
*/
docs?: string;
/**
* Optional actionable instructions on how to resolve the problem.
*/
fix?: string;
/**
* Locations in user code that contributed to this diagnostic, in
* `file:line:column` format.
*/
sources?: string[];
/**
* Alias for {@link Error.message}: the reason this diagnostic was raised.
*/
get why(): string;
/**
* @param init structured initializer; `why` is required
* @param captureFrom V8 stack-cutoff frame. Defaults to {@link Diagnostic}
* so the top of the trace is the `new Diagnostic(...)` call site.
* `defineDiagnostics` passes its action method to strip its own frames too.
* Ignored on engines without `Error.captureStackTrace`.
*/
constructor(init: DiagnosticInit, captureFrom?: Function);
/**
* Converts the diagnostic into a serializable structured object.
*/
toJSON(): object;
}
/**
* Creates a typed diagnostics object from a set of code definitions. Each
* code becomes a callable {@link DiagnosticHandle}: invoke to report, or
* `throw` the result to raise. No `new` required, no proxy.
*/
declare function defineDiagnostics<const Codes extends Record<string, DiagnosticDefinition>, const Reporters extends readonly AnyDiagnosticReporter[]>(options: DefineDiagnosticsOptions<Codes, Reporters>): Diagnostics<Codes, Reporters>;
/**
* Extracts the options object a reporter accepts as its 2nd argument. Returns
* `{}` when the reporter has no 2nd arg (so it contributes nothing to the
* merged shape).
*/
type ExtractSingleReporterOptions<Reporter> = Reporter extends ((diagnostic: Diagnostic, options: infer ReporterOpts) => any) ? IsUnknown<ReporterOpts> extends true ? {} : Exclude<ReporterOpts, undefined> : {};
/**
* Intersects every reporter's options shape into a single object. If any
* reporter has a required field, the merged shape has a required field, and
* {@link ActionArgs} flips `reporterOptions` from optional to required via
* `{} extends Merged`.
*/
type ExtractReportersOptions<Reporters extends readonly any[]> = Reporters extends readonly [infer First, ...infer Rest] ? ExtractSingleReporterOptions<First> & ExtractReportersOptions<Rest> : {};
//#endregion
export { DiagnosticCallParams as a, DiagnosticInit as c, defineDiagnostics as d, ValueOrFn as f, Diagnostic as i, DiagnosticReporter as l, ConsoleReporterOptions as n, DiagnosticDefinition as o, DefineDiagnosticsOptions as r, DiagnosticHandle as s, ConsoleMethod as t, createConsoleReporter as u };
//# sourceMappingURL=diagnostic-ChOW5Avm.d.mts.map
# Migrate errors and warnings to nostics
Follow this start to finish whenever the task is migrating a library's existing user-facing errors, warnings, and logs (`console.warn`/`console.error`, `warn()` helpers, thrown `Error`s) to nostics diagnostics. Turn ad hoc reporting into a catalog of stable diagnostic codes **without changing runtime behavior**. Full API: the `nostics` skill's `SKILL.md` (this reference lives beside it).
## What to migrate
Inventory `console.warn`, `console.error`, `warn(...)` helpers, `throw new Error(...)`, `Promise.reject(new Error(...))`. Skip tests, fixtures, snapshots, and generated output. Plain debug `console.log`s are usually not user-facing; leave them.
Migrate:
- dev warnings that report and keep going
- warnings followed by recovery or a fallback (replace only the report, keep the recovery)
- plain user-facing thrown or rejected `Error`s (the diagnostic becomes the thrown/rejected value)
- deprecation notices
- build/config errors caused by a user's file: always pass **both** the original error as `cause` and the file as `sources`, because the JS stack points inside the library and is useless to the user
Do **not** migrate:
- **structured errors other code inspects** (type fields, private symbols, `isXxxError()` guards, `instanceof` checks): they are control flow, not reporting. Leave them unchanged. Only if such an error is also deliberately user-facing, add a separate dev-only report with the error as `cause`; never replace the error object itself.
- **catch blocks that only log a native/platform error and fall back** when the library cannot name a likely cause or a concrete fix: the native error is the best available information, keep the plain log. This exception covers platform APIs failing (e.g. `history.pushState`, storage quota), **not** errors caused by the user's own files: a caught parse or config error on a user file should become a diagnostic carrying the original error as `cause` and the file as `sources`.
- anything where the diagnostic would only restate "an operation failed". A diagnostic earns its place by naming a likely user-code problem or a concrete fix.
## Preserve behavior exactly
A project's dev guard may be `process.env.NODE_ENV !== 'production'` or its own build-time constant (libraries often define a flag for this; recognize whatever the codebase uses). Written as `DEV` below; treat all forms the same:
- Keep existing dev guards exactly as they are. nostics stripping is additive and does not replace them. If a throw or reject only happened in dev, it must still only happen in dev.
- Never add a guard the original did not have. A throw or report that fired in production keeps firing in production builds that do not use stripping; note that migrating an unguarded report-only call makes it strippable, so once `nosticsStrip` runs in the build it disappears from production bundles. That is usually the goal of the migration, but if the library deliberately reports in production, surface that decision instead of changing it silently.
- Keep throw vs reject, timing, recovery code, and returned fallbacks.
- Keep structured error shapes (fields, symbols). Migrating a throw replaces the thrown message with the diagnostic's `why`: if tests assert the exact old text, update them deliberately as part of the migration, never weaken the message to dodge a test.
## Catalog shape
One catalog file per area of the library (a single `src/diagnostics.ts` is fine for small ones), exported directly. No factory wrappers and no deep barrel re-exports: the strip plugin tracks the export across one relative import. `nostics` is a runtime import: add it to `dependencies`, not `devDependencies` (library bundlers refuse or inline it otherwise).
```ts
import { createConsoleReporter, defineDiagnostics } from 'nostics'
export const diagnostics = /*#__PURE__*/ defineDiagnostics({
docsBase: (code) => `https://example.com/e/${code.toLowerCase()}`,
reporters: [/*#__PURE__*/ createConsoleReporter()],
codes: {
LIB_R0001: {
why: (p: { hook: string }) => `${p.hook}() must be called at the top of a setup function.`,
fix: 'Move the call into setup() or a composable called by setup().',
},
},
})
```
- Codes are `PREFIX_XNNNN`. Pick the category letter by **area**, not severity: `B` build, `R` runtime, `C` config, `D` deprecation. A runtime warning is `R`; reserve `D` for deprecations. Published codes are permanent: never rename or reuse one.
- `why` says what happened with runtime values interpolated through typed param functions (both `why` and `fix` accept them; their params are merged and required at the call site). `fix` is the concrete next action, never a restatement of the problem.
- Extra `console.warn`/`console.error` arguments must not be lost: an error value becomes `cause`; data values are interpolated into `why` (e.g. `JSON.stringify(p.value)`).
- `cause` and `sources` (`'file:line:column'` strings pointing at user code) go **inside the params object** (the first argument), merged with the message params. The second argument is reporter options only, e.g. `{ method: 'error' }`.
- `docsBase` is optional. If the project has no documented error-page URL scheme, propose one and surface it to the maintainer rather than inventing pages that do not exist.
## Call-site patterns
| Before | After |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `DEV && warn(msg)` | `DEV && diagnostics.LIB_R0001(params)` (same guard) |
| warn, then recover/fallback | diagnostic, then the same recovery |
| warn, then `throw new Error(...)` | `throw diagnostics.LIB_R0002(params)` |
| warn, then `Promise.reject(new Error(...))` | `return Promise.reject(diagnostics.LIB_R0003(params))` |
| `console.error(...)` level | `diagnostics.LIB_B0001(params, { method: 'error' })` |
| caught error tied to a user file | `diagnostics.LIB_B0002({ ...params, cause: err, sources: ['src/file.ts:10:5'] }, { method: 'error' })` |
| structured/internal error | leave unchanged |
Calling a handle always runs the reporters, so `throw diagnostics.CODE(params)` reports **and** throws. For a warn-then-throw site that is the same double output it already had; for a bare `throw new Error(...)` it adds a console report before the throw, which is normally fine but worth mentioning if the library is strict about console output.
Report-only calls must stay bare expression statements (`DEV && diagnostics.LIB_R0001(p)` included) so `nosticsStrip` can remove them in production. `throw`/`return`/assigned diagnostics are behavior and stay.
Dropping diagnostics from production builds takes two pieces: `/*#__PURE__*/` annotations on the catalog (`defineDiagnostics(...)` and each reporter factory call inside it) so an unused catalog tree-shakes away, and a `DEV` guard on every report-only call site. Both can be written manually in source, or the `nosticsStrip` build plugin adds them at build time (`import { nosticsStrip } from '@nostics/unplugin/strip-transform'`, then the matching unplugin adapter: `nosticsStrip.rolldown()`, `.vite()`, `.rollup()`, ...). Decide from context: when every report-only site is already dev-guarded, manual annotations in the catalog file are enough and avoid a build transform; reach for the plugin when call sites are unguarded and stripping is wanted. Either way the behavior rule holds: if the library deliberately reports unguarded in production, do not silence it with a guard or the plugin; ask the maintainer.
## Verify
- Tests for warnings, throws, guards, and error shapes still pass; tests asserting exact message text are updated consciously, not accidentally.
- Dev-only gates are still present everywhere the source had them, and no new gates were added.
- Report-only diagnostics remain strippable expression statements. Thrown/returned diagnostics keep their message text in production by design: they are behavior, not reports.
+1
-1

@@ -1,2 +0,2 @@

import { i as Diagnostic } from "../diagnostic-6-n-OsUx.mjs";
import { i as Diagnostic } from "../diagnostic-ChOW5Avm.mjs";

@@ -3,0 +3,0 @@ //#region src/formatters/ansi.d.ts

@@ -1,1 +0,1 @@

{"version":3,"file":"ansi.mjs","names":[],"sources":["../../src/formatters/ansi.ts"],"sourcesContent":["import type { Diagnostic } from '../diagnostic'\n\nexport interface Colors {\n red: (s: string) => string\n yellow: (s: string) => string\n cyan: (s: string) => string\n gray: (s: string) => string\n bold: (s: string) => string\n dim: (s: string) => string\n}\n\n/* @__NO_SIDE_EFFECTS__ */\nexport function ansiFormatter(colors: Colors): (d: Diagnostic) => string {\n return (d) => {\n const tag = colors.bold(colors.red(`[${d.name}]`))\n const header = `${tag} ${d.message}`\n\n const details: string[] = []\n if (d.fix)\n details.push(`${colors.dim('fix:')} ${d.fix}`)\n if (d.sources?.length)\n details.push(`${colors.dim('sources:')} ${d.sources.join(', ')}`)\n if (d.docs)\n details.push(`${colors.dim('see:')} ${colors.cyan(d.docs)}`)\n\n if (details.length === 0)\n return header\n\n const lines = details.map((detail, i) => {\n const connector = colors.dim(i < details.length - 1 ? '├▶' : '╰▶')\n return `${connector} ${detail}`\n })\n return [header, ...lines].join('\\n')\n }\n}\n"],"mappings":";;AAYA,SAAgB,cAAc,QAA2C;CACvE,QAAQ,MAAM;EAEZ,MAAM,SAAS,GADH,OAAO,KAAK,OAAO,IAAI,IAAI,EAAE,KAAK,GAAG,CAC5B,CAAC,GAAG,EAAE;EAE3B,MAAM,UAAoB,EAAE;EAC5B,IAAI,EAAE,KACJ,QAAQ,KAAK,GAAG,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,MAAM;EAChD,IAAI,EAAE,SAAS,QACb,QAAQ,KAAK,GAAG,OAAO,IAAI,WAAW,CAAC,GAAG,EAAE,QAAQ,KAAK,KAAK,GAAG;EACnE,IAAI,EAAE,MACJ,QAAQ,KAAK,GAAG,OAAO,IAAI,OAAO,CAAC,GAAG,OAAO,KAAK,EAAE,KAAK,GAAG;EAE9D,IAAI,QAAQ,WAAW,GACrB,OAAO;EAMT,OAAO,CAAC,QAAQ,GAJF,QAAQ,KAAK,QAAQ,MAAM;GAEvC,OAAO,GADW,OAAO,IAAI,IAAI,QAAQ,SAAS,IAAI,OAAO,KAC1C,CAAC,GAAG;IAED,CAAC,CAAC,KAAK,KAAK"}
{"version":3,"file":"ansi.mjs","names":[],"sources":["../../src/formatters/ansi.ts"],"sourcesContent":["import type { Diagnostic } from '../diagnostic'\n\nexport interface Colors {\n red: (s: string) => string\n yellow: (s: string) => string\n cyan: (s: string) => string\n gray: (s: string) => string\n bold: (s: string) => string\n dim: (s: string) => string\n}\n\n/* @__NO_SIDE_EFFECTS__ */\nexport function ansiFormatter(colors: Colors): (d: Diagnostic) => string {\n return (d) => {\n const tag = colors.bold(colors.red(`[${d.name}]`))\n const header = `${tag} ${d.message}`\n\n const details: string[] = []\n if (d.fix)\n details.push(`${colors.dim('fix:')} ${d.fix}`)\n if (d.sources?.length)\n details.push(`${colors.dim('sources:')} ${d.sources.join(', ')}`)\n if (d.docs)\n details.push(`${colors.dim('see:')} ${colors.cyan(d.docs)}`)\n\n if (details.length === 0)\n return header\n\n const lines = details.map((detail, i) => {\n const connector = colors.dim(i < details.length - 1 ? '├▶' : '╰▶')\n return `${connector} ${detail}`\n })\n return [header, ...lines].join('\\n')\n }\n}\n"],"mappings":";;AAYA,SAAgB,cAAc,QAA2C;CACvE,QAAQ,MAAM;EAEZ,MAAM,SAAS,GADH,OAAO,KAAK,OAAO,IAAI,IAAI,EAAE,KAAK,EAAE,CAC5B,EAAE,GAAG,EAAE;EAE3B,MAAM,UAAoB,CAAC;EAC3B,IAAI,EAAE,KACJ,QAAQ,KAAK,GAAG,OAAO,IAAI,MAAM,EAAE,GAAG,EAAE,KAAK;EAC/C,IAAI,EAAE,SAAS,QACb,QAAQ,KAAK,GAAG,OAAO,IAAI,UAAU,EAAE,GAAG,EAAE,QAAQ,KAAK,IAAI,GAAG;EAClE,IAAI,EAAE,MACJ,QAAQ,KAAK,GAAG,OAAO,IAAI,MAAM,EAAE,GAAG,OAAO,KAAK,EAAE,IAAI,GAAG;EAE7D,IAAI,QAAQ,WAAW,GACrB,OAAO;EAMT,OAAO,CAAC,QAAQ,GAJF,QAAQ,KAAK,QAAQ,MAAM;GAEvC,OAAO,GADW,OAAO,IAAI,IAAI,QAAQ,SAAS,IAAI,OAAO,IAC3C,EAAE,GAAG;EACzB,CACuB,CAAC,CAAC,CAAC,KAAK,IAAI;CACrC;AACF"}

@@ -1,2 +0,2 @@

import { i as Diagnostic } from "../diagnostic-6-n-OsUx.mjs";
import { i as Diagnostic } from "../diagnostic-ChOW5Avm.mjs";

@@ -3,0 +3,0 @@ //#region src/formatters/json.d.ts

@@ -1,1 +0,1 @@

{"version":3,"file":"json.mjs","names":[],"sources":["../../src/formatters/json.ts"],"sourcesContent":["import type { Diagnostic } from '../diagnostic'\n\nexport const jsonFormatter = (d: Diagnostic): string => JSON.stringify(d)\n"],"mappings":";AAEA,MAAa,iBAAiB,MAA0B,KAAK,UAAU,EAAE"}
{"version":3,"file":"json.mjs","names":[],"sources":["../../src/formatters/json.ts"],"sourcesContent":["import type { Diagnostic } from '../diagnostic'\n\nexport const jsonFormatter = (d: Diagnostic): string => JSON.stringify(d)\n"],"mappings":";AAEA,MAAa,iBAAiB,MAA0B,KAAK,UAAU,CAAC"}

@@ -1,2 +0,2 @@

import { a as DiagnosticCallParams, c as DiagnosticInit, d as defineDiagnostics, f as reporterLog, i as Diagnostic, l as DiagnosticReporter, n as ConsoleReporterOptions, o as DiagnosticDefinition, p as ValueOrFn, r as DefineDiagnosticsOptions, s as DiagnosticHandle, t as ConsoleMethod, u as createConsoleReporter } from "./diagnostic-6-n-OsUx.mjs";
import { a as DiagnosticCallParams, c as DiagnosticInit, d as defineDiagnostics, f as ValueOrFn, i as Diagnostic, l as DiagnosticReporter, n as ConsoleReporterOptions, o as DiagnosticDefinition, r as DefineDiagnosticsOptions, s as DiagnosticHandle, t as ConsoleMethod, u as createConsoleReporter } from "./diagnostic-ChOW5Avm.mjs";

@@ -11,3 +11,3 @@ //#region src/formatters/plain.d.ts

//#endregion
export { type ConsoleMethod, type ConsoleReporterOptions, type DefineDiagnosticsOptions, Diagnostic, type DiagnosticCallParams, type DiagnosticDefinition, type DiagnosticHandle, type DiagnosticInit, type DiagnosticReporter, type ValueOrFn as _ValueOrFn, createConsoleReporter, defineDiagnostics, formatDiagnostic, reporterLog };
export { type ConsoleMethod, type ConsoleReporterOptions, type DefineDiagnosticsOptions, Diagnostic, type DiagnosticCallParams, type DiagnosticDefinition, type DiagnosticHandle, type DiagnosticInit, type DiagnosticReporter, type ValueOrFn as _ValueOrFn, createConsoleReporter, defineDiagnostics, formatDiagnostic };
//# sourceMappingURL=index.d.mts.map

@@ -45,8 +45,2 @@ //#region src/formatters/plain.ts

}
/**
* Ready-made console reporter, equivalent to `createConsoleReporter()`.
*
* @deprecated Use `createConsoleReporter()` instead.
*/
const reporterLog = /* @__PURE__ */ createConsoleReporter();
const captureStackTrace = Error.captureStackTrace;

@@ -134,4 +128,4 @@ var Diagnostic = class Diagnostic extends Error {

//#endregion
export { Diagnostic, createConsoleReporter, defineDiagnostics, formatDiagnostic, reporterLog };
export { Diagnostic, createConsoleReporter, defineDiagnostics, formatDiagnostic };
//# sourceMappingURL=index.mjs.map

@@ -1,1 +0,1 @@

{"version":3,"file":"index.mjs","names":[],"sources":["../src/formatters/plain.ts","../src/utils.ts","../src/diagnostic.ts"],"sourcesContent":["import type { Diagnostic } from '../diagnostic'\n\n/**\n * Renders a diagnostic into a multi-line, unicode-decorated string suitable\n * for terminal output. The first line is `[<name>] <message>`; optional\n * details (`fix`, `sources`, `docs`) follow with `├▶`/`╰▶` connectors.\n */\nexport function formatDiagnostic(diagnostic: Diagnostic): string {\n const header = `[${diagnostic.name}] ${diagnostic.message}`\n\n const details: string[] = []\n if (diagnostic.fix) {\n details.push(`fix: ${diagnostic.fix}`)\n }\n if (diagnostic.sources?.length) {\n details.push(`sources: ${diagnostic.sources.join(', ')}`)\n }\n if (diagnostic.docs) {\n details.push(`see: ${diagnostic.docs}`)\n }\n\n if (details.length === 0) {\n return header\n }\n\n const lines = details.map((detail, i) => {\n const connector = i < details.length - 1 ? '├▶' : '╰▶'\n return `${connector} ${detail}`\n })\n\n return [header, ...lines].join('\\n')\n}\n","/**\n * Transforms a value or a function that returns a value to a value.\n *\n * @param valFn either a value or a function that returns a value\n * @param args arguments to pass to the function if `valFn` is a function\n *\n * @internal\n */\nexport function toValueWithArgs<T, Args extends any[]>(\n valFn: T | ((...args: Args) => T),\n ...args: Args\n): T {\n return typeof valFn === 'function' ? (valFn as (...args: Args) => T)(...args) : valFn\n}\n\n/**\n * A value of type T, or a function that resolves T from a single params object.\n *\n * @internal\n */\nexport type ValueOrFn<T, P = any> = T | ((params: P) => T)\n\n/**\n * Extracts the param type from a single-arg function, or `never` for\n * non-function inputs. Pairs with {@link ValueOrFn}.\n *\n * @internal\n */\nexport type ExtractFnParam<T> = T extends (params: infer P) => any ? P : never\n\n/**\n * Converts a union of types to their intersection.\n *\n * @internal\n */\nexport type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (\n k: infer I,\n) => void\n ? I\n : never\n\n/**\n * `true` when `T` is the `any` type.\n *\n * @internal\n */\nexport type IsAny<Type> = 0 extends 1 & Type ? true : false\n\n/**\n * `true` when `T` is the `unknown` type (and not `any`).\n *\n * @internal\n */\nexport type IsUnknown<Type> = IsAny<Type> extends true ? false : unknown extends Type ? true : false\n\n/**\n * Expands a type to its property listing so editor hovers show the resolved\n * shape instead of a chain of aliases / intersections.\n *\n * @internal\n */\nexport type Prettify<Type> = {\n [Key in keyof Type]: Type[Key]\n}\n","/* eslint-disable ts/no-empty-object-type -- `{}` is used as the neutral element when intersecting reporter option shapes */\n/* eslint-disable ts/no-unsafe-function-type -- used by captureStackTrace */\n\nimport type { ExtractFnParam, IsUnknown, Prettify, UnionToIntersection, ValueOrFn } from './utils'\nimport { formatDiagnostic } from './formatters/plain'\nimport { toValueWithArgs } from './utils'\n\n/**\n * Define-time shape of a diagnostic. Each field can be a static value or a\n * function that resolves it from a shared `params` object passed at call\n * time. Runtime-only fields (`cause`, `sources`) from {@link DiagnosticInit}\n * are intentionally omitted: they're only meaningful at the call site.\n */\nexport interface DiagnosticDefinition<P = any> {\n /**\n * The error message: why this failed. String, or a function of `params`.\n *\n * @example\n * ```ts\n * why: (p: { name: string }) => `module \"${p.name}\" failed to load`\n * ```\n */\n why: ValueOrFn<string, P>\n\n /**\n * Actionable instructions on how to resolve the problem. String, or a\n * function of `params`.\n *\n * @example\n * ```ts\n * fix: (p: { name: string }) => `run \"npm install ${p.name}\"`\n * ```\n */\n fix?: ValueOrFn<string, P>\n\n /**\n * Per-code docs URL. A string overrides\n * {@link DefineDiagnosticsOptions.docsBase} for this code; `false` opts this\n * code out entirely, even when `docsBase` is set. When omitted, the URL is\n * derived from `docsBase`.\n */\n docs?: string | false\n}\n\n/**\n * Runtime-only fields that can be passed alongside the interpolation params\n * at call time. Merged into the same object so callers pass everything in\n * one place.\n */\nexport interface DiagnosticCallParams {\n /**\n * Original error or exception that triggered this diagnostic. Pass it\n * through when re-throwing so the original stack trace is preserved.\n */\n cause?: unknown\n\n /**\n * Locations in user code that contributed to this diagnostic, in\n * `file:line:column` format. Useful for compilers and other tools where the\n * JS stack trace doesn't reflect the user's source.\n */\n sources?: string[]\n}\n\n/**\n * Structured initializer for a {@link Diagnostic}. `why` is the only required\n * field: it becomes the {@link Diagnostic.message}. The remaining fields are\n * optional metadata that reporters and consumers can render or forward.\n */\nexport interface DiagnosticInit extends DiagnosticCallParams {\n /**\n * The actual error message: why this failed.\n * Mirrored to `Error.message`.\n */\n why: string\n\n /**\n * Optional actionable instructions on how to resolve the problem.\n */\n fix?: string\n\n /**\n * URL to extended documentation for this diagnostic.\n */\n docs?: string\n}\n\n/**\n * Represents how to report a diagnostic. Could call `console.log()`, send the\n * diagnostic to a server, or something else. Reporters declare the shape of\n * options they need via `ReporterOpts`; `defineDiagnostics` intersects every\n * reporter's options into a single object passed at the call site.\n */\nexport type DiagnosticReporter<ReporterOpts extends object = {}> = (\n diagnostic: Diagnostic,\n options: ReporterOpts,\n) => void\n\n/**\n * Permissive reporter constraint used internally so reporters with 1 arg,\n * required options, or optional options all satisfy the array constraint.\n *\n * @internal\n */\ntype AnyDiagnosticReporter = (diagnostic: Diagnostic, options: any) => void\n\n/**\n * The `console` methods a log reporter can route to.\n */\nexport type ConsoleMethod = 'log' | 'error' | 'warn'\n\n/**\n * Options for {@link createConsoleReporter}.\n */\nexport interface ConsoleReporterOptions {\n /**\n * `console` method used to print the diagnostic. Defaults to `'warn'`. The\n * returned reporter still accepts a per-call `{ method }` override through\n * the call-site reporter options.\n */\n method?: ConsoleMethod\n\n /**\n * Renders the diagnostic into the string handed to `console`. Defaults to\n * {@link formatDiagnostic}, the plain unicode-decorated formatter.\n */\n formatter?: (diagnostic: Diagnostic) => string\n}\n\n/**\n * Creates a console reporter that renders each diagnostic with `formatter` and\n * prints the result via `console[method]`. Both default sensibly (`'warn'` and\n * {@link formatDiagnostic}); `method` can also be overridden per call through\n * the reporter options.\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function createConsoleReporter({\n method: defaultMethod = 'warn',\n formatter = formatDiagnostic,\n}: ConsoleReporterOptions = {}): DiagnosticReporter<{ method?: ConsoleMethod }> {\n return (diagnostic, { method = defaultMethod } = {}) => {\n // eslint-disable-next-line no-console\n console[method](formatter(diagnostic))\n }\n}\n\n/**\n * Ready-made console reporter, equivalent to `createConsoleReporter()`.\n *\n * @deprecated Use `createConsoleReporter()` instead.\n */\nexport const reporterLog: DiagnosticReporter<{ method?: ConsoleMethod }> = createConsoleReporter()\n\n/**\n * Resolves the `params` type a code expects from the intersection of params\n * across all function-typed fields, falling back to `{}` when every field is\n * static. Merged with {@link DiagnosticCallParams} at the call site.\n *\n * @internal\n */\ntype InferCodeParams<Def> = [ExtractFnParam<Def[keyof Def]>] extends [never]\n ? {}\n : UnionToIntersection<ExtractFnParam<Def[keyof Def]>>\n\n/**\n * Options for {@link defineDiagnostics}.\n */\nexport interface DefineDiagnosticsOptions<\n Codes extends Record<string, DiagnosticDefinition>,\n Reporters extends readonly AnyDiagnosticReporter[],\n> {\n /**\n * Base URL or resolver for documentation links. When a string, the code is\n * appended as a lowercase path segment (e.g. `\"https://docs.example.com\"` →\n * `\"https://docs.example.com/math_e001\"`). When a function, receives the\n * code and returns a URL or `undefined`.\n */\n docsBase?: string | ((code: keyof Codes) => string | undefined)\n\n /**\n * Map of diagnostic codes to their definitions.\n */\n codes: Codes\n\n /**\n * Reporters called every time a diagnostic is produced. Can be used to\n * integrate with custom logging.\n */\n reporters?: Reporters\n}\n\n/**\n * The first positional argument of a {@link DiagnosticHandle} call:\n * interpolation params merged with the runtime-only call-site fields\n * (`cause`, `sources`).\n *\n * @internal\n */\ntype CallSiteParams<Params> = Params & DiagnosticCallParams\n\n/**\n * Resolves the full argument tuple for a {@link DiagnosticHandle} call.\n * Branches on whether params and reporter options each have required fields.\n * Required positions become required tuple elements, all-optional ones\n * become `?`, and when no reporter declares any options the parameter is\n * omitted entirely.\n *\n * @internal\n */\ntype ActionArgs<Params, ReporterOpts> = keyof ReporterOpts extends never\n ? {} extends Params\n ? [params?: CallSiteParams<Params>]\n : [params: CallSiteParams<Params>]\n : {} extends ReporterOpts\n ? {} extends Params\n ? [params?: CallSiteParams<Params>, reporterOptions?: ReporterOpts]\n : [params: CallSiteParams<Params>, reporterOptions?: ReporterOpts]\n : {} extends Params\n ? [params: CallSiteParams<Params> | undefined, reporterOptions: ReporterOpts]\n : [params: CallSiteParams<Params>, reporterOptions: ReporterOpts]\n\n/**\n * Per-code handle exposed by {@link defineDiagnostics}. Each code is a\n * callable: invoke it to build the diagnostic and run every reporter, or\n * prefix the call with `throw` to raise it.\n *\n * @example\n * ```ts\n * diagnostics.MATH_E001({ name: 'x' }) // report\n * throw diagnostics.MATH_E001({ name: 'x' }) // throw\n * ```\n */\nexport interface DiagnosticHandle<Params, ReporterOpts> {\n /**\n * Builds the diagnostic, runs every reporter, and returns the diagnostic\n * instance. The returned diagnostic can be inspected, attached as `cause`,\n * or thrown with `throw`.\n */\n (...args: ActionArgs<Params, ReporterOpts>): Diagnostic\n}\n\n/**\n * Return type of {@link defineDiagnostics}.\n */\ntype Diagnostics<\n Codes extends Record<string, DiagnosticDefinition>,\n Reporters extends readonly AnyDiagnosticReporter[],\n> = {\n [Code in keyof Codes]: DiagnosticHandle<\n InferCodeParams<Codes[Code]>,\n Prettify<ExtractReportersOptions<Reporters>>\n >\n}\n\nconst captureStackTrace = (\n Error as { captureStackTrace?: (target: object, frame: Function) => void }\n).captureStackTrace\n\nexport class Diagnostic extends Error {\n name: string = 'Diagnostic'\n\n /**\n * URL to extended documentation for this diagnostic code.\n * Auto-generated from {@link DefineDiagnosticsOptions.docsBase}.\n */\n docs?: string\n\n /**\n * Optional actionable instructions on how to resolve the problem.\n */\n fix?: string\n\n /**\n * Locations in user code that contributed to this diagnostic, in\n * `file:line:column` format.\n */\n sources?: string[]\n\n /**\n * Alias for {@link Error.message}: the reason this diagnostic was raised.\n */\n get why(): string {\n return this.message\n }\n\n /**\n * @param init structured initializer; `why` is required\n * @param captureFrom V8 stack-cutoff frame. Defaults to {@link Diagnostic}\n * so the top of the trace is the `new Diagnostic(...)` call site.\n * `defineDiagnostics` passes its action method to strip its own frames too.\n * Ignored on engines without `Error.captureStackTrace`.\n */\n constructor(init: DiagnosticInit, captureFrom: Function = Diagnostic) {\n super(init.why, { cause: init.cause })\n this.fix = init.fix\n this.docs = init.docs\n this.sources = init.sources\n // V8-only API, but also implemented pretty much everywhere. Worst case\n // scenario, we fall back to the stack `Error` captures by default, which\n // includes a couple of extra internal frames but is still usable.\n captureStackTrace?.(this, captureFrom)\n }\n\n /**\n * Converts the diagnostic into a serializable structured object.\n */\n toJSON(): object {\n return {\n name: this.name,\n why: this.why,\n fix: this.fix,\n docs: this.docs,\n sources: this.sources,\n cause: this.cause,\n stack: this.stack,\n }\n }\n}\n\n/**\n * Creates a typed diagnostics object from a set of code definitions. Each\n * code becomes a callable {@link DiagnosticHandle}: invoke to report, or\n * `throw` the result to raise. No `new` required, no proxy.\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function defineDiagnostics<\n const Codes extends Record<string, DiagnosticDefinition>,\n const Reporters extends readonly AnyDiagnosticReporter[],\n>(options: DefineDiagnosticsOptions<Codes, Reporters>): Diagnostics<Codes, Reporters> {\n const reporters = options.reporters ?? []\n const result = {} as Diagnostics<Codes, Reporters>\n\n const { docsBase } = options\n\n for (const code of Object.keys(options.codes) as Extract<keyof Codes, string>[]) {\n const def = options.codes[code]\n // skip docs if set to false, otherwise use it or derive it from docsBase\n const docs\n = def.docs === false\n ? undefined\n : def.docs\n || (typeof docsBase === 'string' ? `${docsBase}/${code.toLowerCase()}` : docsBase?.(code))\n\n const handle = (\n params: DiagnosticCallParams & Record<string, unknown> = {},\n reporterOptions: any = {},\n ): Diagnostic => {\n const diagnostic = new Diagnostic(\n {\n why: toValueWithArgs(def.why, params),\n fix: toValueWithArgs(def.fix, params),\n docs,\n cause: params.cause,\n sources: params.sources,\n },\n handle,\n )\n diagnostic.name = code\n for (const reporter of reporters) reporter(diagnostic, reporterOptions)\n return diagnostic\n }\n\n result[code] = handle as unknown as Diagnostics<Codes, Reporters>[typeof code]\n }\n\n return result\n}\n\n/**\n * Extracts the options object a reporter accepts as its 2nd argument. Returns\n * `{}` when the reporter has no 2nd arg (so it contributes nothing to the\n * merged shape).\n */\ntype ExtractSingleReporterOptions<Reporter> = Reporter extends (\n diagnostic: Diagnostic,\n options: infer ReporterOpts,\n) => any\n ? IsUnknown<ReporterOpts> extends true\n ? {}\n : Exclude<ReporterOpts, undefined>\n : {}\n\n/**\n * Intersects every reporter's options shape into a single object. If any\n * reporter has a required field, the merged shape has a required field, and\n * {@link ActionArgs} flips `reporterOptions` from optional to required via\n * `{} extends Merged`.\n */\ntype ExtractReportersOptions<Reporters extends readonly any[]> = Reporters extends readonly [\n infer First,\n ...infer Rest,\n]\n ? ExtractSingleReporterOptions<First> & ExtractReportersOptions<Rest>\n : {}\n"],"mappings":";;;;;;AAOA,SAAgB,iBAAiB,YAAgC;CAC/D,MAAM,SAAS,IAAI,WAAW,KAAK,IAAI,WAAW;CAElD,MAAM,UAAoB,EAAE;CAC5B,IAAI,WAAW,KACb,QAAQ,KAAK,QAAQ,WAAW,MAAM;CAExC,IAAI,WAAW,SAAS,QACtB,QAAQ,KAAK,YAAY,WAAW,QAAQ,KAAK,KAAK,GAAG;CAE3D,IAAI,WAAW,MACb,QAAQ,KAAK,QAAQ,WAAW,OAAO;CAGzC,IAAI,QAAQ,WAAW,GACrB,OAAO;CAQT,OAAO,CAAC,QAAQ,GALF,QAAQ,KAAK,QAAQ,MAAM;EAEvC,OAAO,GADW,IAAI,QAAQ,SAAS,IAAI,OAAO,KAC9B,GAAG;GAGD,CAAC,CAAC,KAAK,KAAK;;;;;;;;;;;;ACtBtC,SAAgB,gBACd,OACA,GAAG,MACA;CACH,OAAO,OAAO,UAAU,aAAc,MAA+B,GAAG,KAAK,GAAG;;;;;;;;;;;AC4HlF,SAAgB,sBAAsB,EACpC,QAAQ,gBAAgB,QACxB,YAAY,qBACc,EAAE,EAAkD;CAC9E,QAAQ,YAAY,EAAE,SAAS,kBAAkB,EAAE,KAAK;EAEtD,QAAQ,QAAQ,UAAU,WAAW,CAAC;;;;;;;;AAS1C,MAAa,cAA8D,uCAAuB;AAuGlG,MAAM,oBACJ,MACA;AAEF,IAAa,aAAb,MAAa,mBAAmB,MAAM;CACpC,OAAe;;;;;CAMf;;;;CAKA;;;;;CAMA;;;;CAKA,IAAI,MAAc;EAChB,OAAO,KAAK;;;;;;;;;CAUd,YAAY,MAAsB,cAAwB,YAAY;EACpE,MAAM,KAAK,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;EACtC,KAAK,MAAM,KAAK;EAChB,KAAK,OAAO,KAAK;EACjB,KAAK,UAAU,KAAK;EAIpB,oBAAoB,MAAM,YAAY;;;;;CAMxC,SAAiB;EACf,OAAO;GACL,MAAM,KAAK;GACX,KAAK,KAAK;GACV,KAAK,KAAK;GACV,MAAM,KAAK;GACX,SAAS,KAAK;GACd,OAAO,KAAK;GACZ,OAAO,KAAK;GACb;;;;;;;;;AAUL,SAAgB,kBAGd,SAAoF;CACpF,MAAM,YAAY,QAAQ,aAAa,EAAE;CACzC,MAAM,SAAS,EAAE;CAEjB,MAAM,EAAE,aAAa;CAErB,KAAK,MAAM,QAAQ,OAAO,KAAK,QAAQ,MAAM,EAAoC;EAC/E,MAAM,MAAM,QAAQ,MAAM;EAE1B,MAAM,OACF,IAAI,SAAS,QACX,KAAA,IACA,IAAI,SACA,OAAO,aAAa,WAAW,GAAG,SAAS,GAAG,KAAK,aAAa,KAAK,WAAW,KAAK;EAE/F,MAAM,UACJ,SAAyD,EAAE,EAC3D,kBAAuB,EAAE,KACV;GACf,MAAM,aAAa,IAAI,WACrB;IACE,KAAK,gBAAgB,IAAI,KAAK,OAAO;IACrC,KAAK,gBAAgB,IAAI,KAAK,OAAO;IACrC;IACA,OAAO,OAAO;IACd,SAAS,OAAO;IACjB,EACD,OACD;GACD,WAAW,OAAO;GAClB,KAAK,MAAM,YAAY,WAAW,SAAS,YAAY,gBAAgB;GACvE,OAAO;;EAGT,OAAO,QAAQ;;CAGjB,OAAO"}
{"version":3,"file":"index.mjs","names":[],"sources":["../src/formatters/plain.ts","../src/utils.ts","../src/diagnostic.ts"],"sourcesContent":["import type { Diagnostic } from '../diagnostic'\n\n/**\n * Renders a diagnostic into a multi-line, unicode-decorated string suitable\n * for terminal output. The first line is `[<name>] <message>`; optional\n * details (`fix`, `sources`, `docs`) follow with `├▶`/`╰▶` connectors.\n */\nexport function formatDiagnostic(diagnostic: Diagnostic): string {\n const header = `[${diagnostic.name}] ${diagnostic.message}`\n\n const details: string[] = []\n if (diagnostic.fix) {\n details.push(`fix: ${diagnostic.fix}`)\n }\n if (diagnostic.sources?.length) {\n details.push(`sources: ${diagnostic.sources.join(', ')}`)\n }\n if (diagnostic.docs) {\n details.push(`see: ${diagnostic.docs}`)\n }\n\n if (details.length === 0) {\n return header\n }\n\n const lines = details.map((detail, i) => {\n const connector = i < details.length - 1 ? '├▶' : '╰▶'\n return `${connector} ${detail}`\n })\n\n return [header, ...lines].join('\\n')\n}\n","/**\n * Transforms a value or a function that returns a value to a value.\n *\n * @param valFn either a value or a function that returns a value\n * @param args arguments to pass to the function if `valFn` is a function\n *\n * @internal\n */\nexport function toValueWithArgs<T, Args extends any[]>(\n valFn: T | ((...args: Args) => T),\n ...args: Args\n): T {\n return typeof valFn === 'function' ? (valFn as (...args: Args) => T)(...args) : valFn\n}\n\n/**\n * A value of type T, or a function that resolves T from a single params object.\n *\n * @internal\n */\nexport type ValueOrFn<T, P = any> = T | ((params: P) => T)\n\n/**\n * Extracts the param type from a single-arg function, or `never` for\n * non-function inputs. Pairs with {@link ValueOrFn}.\n *\n * @internal\n */\nexport type ExtractFnParam<T> = T extends (params: infer P) => any ? P : never\n\n/**\n * Converts a union of types to their intersection.\n *\n * @internal\n */\nexport type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (\n k: infer I,\n) => void\n ? I\n : never\n\n/**\n * `true` when `T` is the `any` type.\n *\n * @internal\n */\nexport type IsAny<Type> = 0 extends 1 & Type ? true : false\n\n/**\n * `true` when `T` is the `unknown` type (and not `any`).\n *\n * @internal\n */\nexport type IsUnknown<Type> = IsAny<Type> extends true ? false : unknown extends Type ? true : false\n\n/**\n * Expands a type to its property listing so editor hovers show the resolved\n * shape instead of a chain of aliases / intersections.\n *\n * @internal\n */\nexport type Prettify<Type> = {\n [Key in keyof Type]: Type[Key]\n}\n","/* eslint-disable ts/no-empty-object-type -- `{}` is used as the neutral element when intersecting reporter option shapes */\n/* eslint-disable ts/no-unsafe-function-type -- used by captureStackTrace */\n\nimport type { ExtractFnParam, IsUnknown, Prettify, UnionToIntersection, ValueOrFn } from './utils'\nimport { formatDiagnostic } from './formatters/plain'\nimport { toValueWithArgs } from './utils'\n\n/**\n * Define-time shape of a diagnostic. Each field can be a static value or a\n * function that resolves it from a shared `params` object passed at call\n * time. Runtime-only fields (`cause`, `sources`) from {@link DiagnosticInit}\n * are intentionally omitted: they're only meaningful at the call site.\n */\nexport interface DiagnosticDefinition<P = any> {\n /**\n * The error message: why this failed. String, or a function of `params`.\n *\n * @example\n * ```ts\n * why: (p: { name: string }) => `module \"${p.name}\" failed to load`\n * ```\n */\n why: ValueOrFn<string, P>\n\n /**\n * Actionable instructions on how to resolve the problem. String, or a\n * function of `params`.\n *\n * @example\n * ```ts\n * fix: (p: { name: string }) => `run \"npm install ${p.name}\"`\n * ```\n */\n fix?: ValueOrFn<string, P>\n\n /**\n * Per-code docs URL. A string overrides\n * {@link DefineDiagnosticsOptions.docsBase} for this code; `false` opts this\n * code out entirely, even when `docsBase` is set. When omitted, the URL is\n * derived from `docsBase`.\n */\n docs?: string | false\n}\n\n/**\n * Runtime-only fields that can be passed alongside the interpolation params\n * at call time. Merged into the same object so callers pass everything in\n * one place.\n */\nexport interface DiagnosticCallParams {\n /**\n * Original error or exception that triggered this diagnostic. Pass it\n * through when re-throwing so the original stack trace is preserved.\n */\n cause?: unknown\n\n /**\n * Locations in user code that contributed to this diagnostic, in\n * `file:line:column` format. Useful for compilers and other tools where the\n * JS stack trace doesn't reflect the user's source.\n */\n sources?: string[]\n}\n\n/**\n * Structured initializer for a {@link Diagnostic}. `why` is the only required\n * field: it becomes the {@link Diagnostic.message}. The remaining fields are\n * optional metadata that reporters and consumers can render or forward.\n */\nexport interface DiagnosticInit extends DiagnosticCallParams {\n /**\n * The actual error message: why this failed.\n * Mirrored to `Error.message`.\n */\n why: string\n\n /**\n * Optional actionable instructions on how to resolve the problem.\n */\n fix?: string\n\n /**\n * URL to extended documentation for this diagnostic.\n */\n docs?: string\n}\n\n/**\n * Represents how to report a diagnostic. Could call `console.log()`, send the\n * diagnostic to a server, or something else. Reporters declare the shape of\n * options they need via `ReporterOpts`; `defineDiagnostics` intersects every\n * reporter's options into a single object passed at the call site.\n */\nexport type DiagnosticReporter<ReporterOpts extends object = {}> = (\n diagnostic: Diagnostic,\n options: ReporterOpts,\n) => void\n\n/**\n * Permissive reporter constraint used internally so reporters with 1 arg,\n * required options, or optional options all satisfy the array constraint.\n *\n * @internal\n */\ntype AnyDiagnosticReporter = (diagnostic: Diagnostic, options: any) => void\n\n/**\n * The `console` methods a log reporter can route to.\n */\nexport type ConsoleMethod = 'log' | 'error' | 'warn'\n\n/**\n * Options for {@link createConsoleReporter}.\n */\nexport interface ConsoleReporterOptions {\n /**\n * `console` method used to print the diagnostic. Defaults to `'warn'`. The\n * returned reporter still accepts a per-call `{ method }` override through\n * the call-site reporter options.\n */\n method?: ConsoleMethod\n\n /**\n * Renders the diagnostic into the string handed to `console`. Defaults to\n * {@link formatDiagnostic}, the plain unicode-decorated formatter.\n */\n formatter?: (diagnostic: Diagnostic) => string\n}\n\n/**\n * Creates a console reporter that renders each diagnostic with `formatter` and\n * prints the result via `console[method]`. Both default sensibly (`'warn'` and\n * {@link formatDiagnostic}); `method` can also be overridden per call through\n * the reporter options.\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function createConsoleReporter({\n method: defaultMethod = 'warn',\n formatter = formatDiagnostic,\n}: ConsoleReporterOptions = {}): DiagnosticReporter<{ method?: ConsoleMethod }> {\n return (diagnostic, { method = defaultMethod } = {}) => {\n // eslint-disable-next-line no-console\n console[method](formatter(diagnostic))\n }\n}\n\n/**\n * Resolves the `params` type a code expects from the intersection of params\n * across all function-typed fields, falling back to `{}` when every field is\n * static. Merged with {@link DiagnosticCallParams} at the call site.\n *\n * @internal\n */\ntype InferCodeParams<Def> = [ExtractFnParam<Def[keyof Def]>] extends [never]\n ? {}\n : UnionToIntersection<ExtractFnParam<Def[keyof Def]>>\n\n/**\n * Options for {@link defineDiagnostics}.\n */\nexport interface DefineDiagnosticsOptions<\n Codes extends Record<string, DiagnosticDefinition>,\n Reporters extends readonly AnyDiagnosticReporter[],\n> {\n /**\n * Base URL or resolver for documentation links. When a string, the code is\n * appended as a lowercase path segment (e.g. `\"https://docs.example.com\"` →\n * `\"https://docs.example.com/math_e001\"`). When a function, receives the\n * code and returns a URL or `undefined`.\n */\n docsBase?: string | ((code: keyof Codes) => string | undefined)\n\n /**\n * Map of diagnostic codes to their definitions.\n */\n codes: Codes\n\n /**\n * Reporters called every time a diagnostic is produced. Can be used to\n * integrate with custom logging.\n */\n reporters?: Reporters\n}\n\n/**\n * The first positional argument of a {@link DiagnosticHandle} call:\n * interpolation params merged with the runtime-only call-site fields\n * (`cause`, `sources`).\n *\n * @internal\n */\ntype CallSiteParams<Params> = Params & DiagnosticCallParams\n\n/**\n * Resolves the full argument tuple for a {@link DiagnosticHandle} call.\n * Branches on whether params and reporter options each have required fields.\n * Required positions become required tuple elements, all-optional ones\n * become `?`, and when no reporter declares any options the parameter is\n * omitted entirely.\n *\n * @internal\n */\ntype ActionArgs<Params, ReporterOpts> = keyof ReporterOpts extends never\n ? {} extends Params\n ? [params?: CallSiteParams<Params>]\n : [params: CallSiteParams<Params>]\n : {} extends ReporterOpts\n ? {} extends Params\n ? [params?: CallSiteParams<Params>, reporterOptions?: ReporterOpts]\n : [params: CallSiteParams<Params>, reporterOptions?: ReporterOpts]\n : {} extends Params\n ? [params: CallSiteParams<Params> | undefined, reporterOptions: ReporterOpts]\n : [params: CallSiteParams<Params>, reporterOptions: ReporterOpts]\n\n/**\n * Per-code handle exposed by {@link defineDiagnostics}. Each code is a\n * callable: invoke it to build the diagnostic and run every reporter, or\n * prefix the call with `throw` to raise it.\n *\n * @example\n * ```ts\n * diagnostics.MATH_E001({ name: 'x' }) // report\n * throw diagnostics.MATH_E001({ name: 'x' }) // throw\n * ```\n */\nexport interface DiagnosticHandle<Params, ReporterOpts> {\n /**\n * Builds the diagnostic, runs every reporter, and returns the diagnostic\n * instance. The returned diagnostic can be inspected, attached as `cause`,\n * or thrown with `throw`.\n */\n (...args: ActionArgs<Params, ReporterOpts>): Diagnostic\n}\n\n/**\n * Return type of {@link defineDiagnostics}.\n */\ntype Diagnostics<\n Codes extends Record<string, DiagnosticDefinition>,\n Reporters extends readonly AnyDiagnosticReporter[],\n> = {\n [Code in keyof Codes]: DiagnosticHandle<\n InferCodeParams<Codes[Code]>,\n Prettify<ExtractReportersOptions<Reporters>>\n >\n}\n\nconst captureStackTrace = (\n Error as { captureStackTrace?: (target: object, frame: Function) => void }\n).captureStackTrace\n\nexport class Diagnostic extends Error {\n name: string = 'Diagnostic'\n\n /**\n * URL to extended documentation for this diagnostic code.\n * Auto-generated from {@link DefineDiagnosticsOptions.docsBase}.\n */\n docs?: string\n\n /**\n * Optional actionable instructions on how to resolve the problem.\n */\n fix?: string\n\n /**\n * Locations in user code that contributed to this diagnostic, in\n * `file:line:column` format.\n */\n sources?: string[]\n\n /**\n * Alias for {@link Error.message}: the reason this diagnostic was raised.\n */\n get why(): string {\n return this.message\n }\n\n /**\n * @param init structured initializer; `why` is required\n * @param captureFrom V8 stack-cutoff frame. Defaults to {@link Diagnostic}\n * so the top of the trace is the `new Diagnostic(...)` call site.\n * `defineDiagnostics` passes its action method to strip its own frames too.\n * Ignored on engines without `Error.captureStackTrace`.\n */\n constructor(init: DiagnosticInit, captureFrom: Function = Diagnostic) {\n super(init.why, { cause: init.cause })\n this.fix = init.fix\n this.docs = init.docs\n this.sources = init.sources\n // V8-only API, but also implemented pretty much everywhere. Worst case\n // scenario, we fall back to the stack `Error` captures by default, which\n // includes a couple of extra internal frames but is still usable.\n captureStackTrace?.(this, captureFrom)\n }\n\n /**\n * Converts the diagnostic into a serializable structured object.\n */\n toJSON(): object {\n return {\n name: this.name,\n why: this.why,\n fix: this.fix,\n docs: this.docs,\n sources: this.sources,\n cause: this.cause,\n stack: this.stack,\n }\n }\n}\n\n/**\n * Creates a typed diagnostics object from a set of code definitions. Each\n * code becomes a callable {@link DiagnosticHandle}: invoke to report, or\n * `throw` the result to raise. No `new` required, no proxy.\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function defineDiagnostics<\n const Codes extends Record<string, DiagnosticDefinition>,\n const Reporters extends readonly AnyDiagnosticReporter[],\n>(options: DefineDiagnosticsOptions<Codes, Reporters>): Diagnostics<Codes, Reporters> {\n const reporters = options.reporters ?? []\n const result = {} as Diagnostics<Codes, Reporters>\n\n const { docsBase } = options\n\n for (const code of Object.keys(options.codes) as Extract<keyof Codes, string>[]) {\n const def = options.codes[code]\n // skip docs if set to false, otherwise use it or derive it from docsBase\n const docs\n = def.docs === false\n ? undefined\n : def.docs\n || (typeof docsBase === 'string' ? `${docsBase}/${code.toLowerCase()}` : docsBase?.(code))\n\n const handle = (\n params: DiagnosticCallParams & Record<string, unknown> = {},\n reporterOptions: any = {},\n ): Diagnostic => {\n const diagnostic = new Diagnostic(\n {\n why: toValueWithArgs(def.why, params),\n fix: toValueWithArgs(def.fix, params),\n docs,\n cause: params.cause,\n sources: params.sources,\n },\n handle,\n )\n diagnostic.name = code\n for (const reporter of reporters) reporter(diagnostic, reporterOptions)\n return diagnostic\n }\n\n result[code] = handle as unknown as Diagnostics<Codes, Reporters>[typeof code]\n }\n\n return result\n}\n\n/**\n * Extracts the options object a reporter accepts as its 2nd argument. Returns\n * `{}` when the reporter has no 2nd arg (so it contributes nothing to the\n * merged shape).\n */\ntype ExtractSingleReporterOptions<Reporter> = Reporter extends (\n diagnostic: Diagnostic,\n options: infer ReporterOpts,\n) => any\n ? IsUnknown<ReporterOpts> extends true\n ? {}\n : Exclude<ReporterOpts, undefined>\n : {}\n\n/**\n * Intersects every reporter's options shape into a single object. If any\n * reporter has a required field, the merged shape has a required field, and\n * {@link ActionArgs} flips `reporterOptions` from optional to required via\n * `{} extends Merged`.\n */\ntype ExtractReportersOptions<Reporters extends readonly any[]> = Reporters extends readonly [\n infer First,\n ...infer Rest,\n]\n ? ExtractSingleReporterOptions<First> & ExtractReportersOptions<Rest>\n : {}\n"],"mappings":";;;;;;AAOA,SAAgB,iBAAiB,YAAgC;CAC/D,MAAM,SAAS,IAAI,WAAW,KAAK,IAAI,WAAW;CAElD,MAAM,UAAoB,CAAC;CAC3B,IAAI,WAAW,KACb,QAAQ,KAAK,QAAQ,WAAW,KAAK;CAEvC,IAAI,WAAW,SAAS,QACtB,QAAQ,KAAK,YAAY,WAAW,QAAQ,KAAK,IAAI,GAAG;CAE1D,IAAI,WAAW,MACb,QAAQ,KAAK,QAAQ,WAAW,MAAM;CAGxC,IAAI,QAAQ,WAAW,GACrB,OAAO;CAQT,OAAO,CAAC,QAAQ,GALF,QAAQ,KAAK,QAAQ,MAAM;EAEvC,OAAO,GADW,IAAI,QAAQ,SAAS,IAAI,OAAO,KAC9B,GAAG;CACzB,CAEuB,CAAC,CAAC,CAAC,KAAK,IAAI;AACrC;;;;;;;;;;;ACvBA,SAAgB,gBACd,OACA,GAAG,MACA;CACH,OAAO,OAAO,UAAU,aAAc,MAA+B,GAAG,IAAI,IAAI;AAClF;;;;;;;;;;AC2HA,SAAgB,sBAAsB,EACpC,QAAQ,gBAAgB,QACxB,YAAY,qBACc,CAAC,GAAmD;CAC9E,QAAQ,YAAY,EAAE,SAAS,kBAAkB,CAAC,MAAM;EAEtD,QAAQ,OAAO,CAAC,UAAU,UAAU,CAAC;CACvC;AACF;AAuGA,MAAM,oBACJ,MACA;AAEF,IAAa,aAAb,MAAa,mBAAmB,MAAM;CACpC,OAAe;;;;;CAMf;;;;CAKA;;;;;CAMA;;;;CAKA,IAAI,MAAc;EAChB,OAAO,KAAK;CACd;;;;;;;;CASA,YAAY,MAAsB,cAAwB,YAAY;EACpE,MAAM,KAAK,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;EACrC,KAAK,MAAM,KAAK;EAChB,KAAK,OAAO,KAAK;EACjB,KAAK,UAAU,KAAK;EAIpB,oBAAoB,MAAM,WAAW;CACvC;;;;CAKA,SAAiB;EACf,OAAO;GACL,MAAM,KAAK;GACX,KAAK,KAAK;GACV,KAAK,KAAK;GACV,MAAM,KAAK;GACX,SAAS,KAAK;GACd,OAAO,KAAK;GACZ,OAAO,KAAK;EACd;CACF;AACF;;;;;;;AAQA,SAAgB,kBAGd,SAAoF;CACpF,MAAM,YAAY,QAAQ,aAAa,CAAC;CACxC,MAAM,SAAS,CAAC;CAEhB,MAAM,EAAE,aAAa;CAErB,KAAK,MAAM,QAAQ,OAAO,KAAK,QAAQ,KAAK,GAAqC;EAC/E,MAAM,MAAM,QAAQ,MAAM;EAE1B,MAAM,OACF,IAAI,SAAS,QACX,KAAA,IACA,IAAI,SACA,OAAO,aAAa,WAAW,GAAG,SAAS,GAAG,KAAK,YAAY,MAAM,WAAW,IAAI;EAE9F,MAAM,UACJ,SAAyD,CAAC,GAC1D,kBAAuB,CAAC,MACT;GACf,MAAM,aAAa,IAAI,WACrB;IACE,KAAK,gBAAgB,IAAI,KAAK,MAAM;IACpC,KAAK,gBAAgB,IAAI,KAAK,MAAM;IACpC;IACA,OAAO,OAAO;IACd,SAAS,OAAO;GAClB,GACA,MACF;GACA,WAAW,OAAO;GAClB,KAAK,MAAM,YAAY,WAAW,SAAS,YAAY,eAAe;GACtE,OAAO;EACT;EAEA,OAAO,QAAQ;CACjB;CAEA,OAAO;AACT"}

@@ -1,2 +0,2 @@

import { l as DiagnosticReporter } from "../diagnostic-6-n-OsUx.mjs";
import { l as DiagnosticReporter } from "../diagnostic-ChOW5Avm.mjs";

@@ -11,10 +11,4 @@ //#region src/reporters/dev.d.ts

declare function createDevReporter(): DiagnosticReporter;
/**
* Ready-made dev reporter, equivalent to `createDevReporter()`.
*
* @deprecated Use `createDevReporter()` instead.
*/
declare const devReporter: DiagnosticReporter;
//#endregion
export { createDevReporter, devReporter };
export { createDevReporter };
//# sourceMappingURL=dev.d.mts.map

@@ -15,11 +15,5 @@ //#region src/reporters/dev.ts

}
/**
* Ready-made dev reporter, equivalent to `createDevReporter()`.
*
* @deprecated Use `createDevReporter()` instead.
*/
const devReporter = /* @__PURE__ */ createDevReporter();
//#endregion
export { createDevReporter, devReporter };
export { createDevReporter };
//# sourceMappingURL=dev.mjs.map

@@ -1,1 +0,1 @@

{"version":3,"file":"dev.mjs","names":[],"sources":["../../src/reporters/dev.ts"],"sourcesContent":["import type { DiagnosticReporter } from '../diagnostic'\n\n/**\n * Creates a reporter for browser code under Vite dev: it forwards each\n * diagnostic over `import.meta.hot.send('nostics:report', ...)` so the\n * dev-server collector can file it. Outside Vite (`import.meta.hot` absent) it\n * warns once and does nothing.\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function createDevReporter(): DiagnosticReporter {\n return (diagnostic) => {\n if (import.meta.hot && typeof import.meta.hot.send === 'function') {\n import.meta.hot.send('nostics:report', diagnostic.toJSON())\n }\n else {\n console.warn(\n '[nostics]: import.meta.hot.send() is not available. This must be running on Vite.',\n )\n }\n }\n}\n\n/**\n * Ready-made dev reporter, equivalent to `createDevReporter()`.\n *\n * @deprecated Use `createDevReporter()` instead.\n */\nexport const devReporter: DiagnosticReporter = createDevReporter()\n"],"mappings":";;;;;;;;AASA,SAAgB,oBAAwC;CACtD,QAAQ,eAAe;EACrB,IAAI,OAAO,KAAK,OAAO,OAAO,OAAO,KAAK,IAAI,SAAS,YACrD,OAAO,KAAK,IAAI,KAAK,kBAAkB,WAAW,QAAQ,CAAC;OAG3D,QAAQ,KACN,oFACD;;;;;;;;AAUP,MAAa,cAAkC,mCAAmB"}
{"version":3,"file":"dev.mjs","names":[],"sources":["../../src/reporters/dev.ts"],"sourcesContent":["import type { DiagnosticReporter } from '../diagnostic'\n\n/**\n * Creates a reporter for browser code under Vite dev: it forwards each\n * diagnostic over `import.meta.hot.send('nostics:report', ...)` so the\n * dev-server collector can file it. Outside Vite (`import.meta.hot` absent) it\n * warns once and does nothing.\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function createDevReporter(): DiagnosticReporter {\n return (diagnostic) => {\n if (import.meta.hot && typeof import.meta.hot.send === 'function') {\n import.meta.hot.send('nostics:report', diagnostic.toJSON())\n }\n else {\n console.warn(\n '[nostics]: import.meta.hot.send() is not available. This must be running on Vite.',\n )\n }\n }\n}\n"],"mappings":";;;;;;;;AASA,SAAgB,oBAAwC;CACtD,QAAQ,eAAe;EACrB,IAAI,OAAO,KAAK,OAAO,OAAO,OAAO,KAAK,IAAI,SAAS,YACrD,OAAO,KAAK,IAAI,KAAK,kBAAkB,WAAW,OAAO,CAAC;OAG1D,QAAQ,KACN,mFACF;CAEJ;AACF"}

@@ -1,2 +0,2 @@

import { l as DiagnosticReporter } from "../diagnostic-6-n-OsUx.mjs";
import { l as DiagnosticReporter } from "../diagnostic-ChOW5Avm.mjs";

@@ -3,0 +3,0 @@ //#region src/reporters/fetch.d.ts

@@ -1,1 +0,1 @@

{"version":3,"file":"fetch.mjs","names":[],"sources":["../../src/reporters/fetch.ts"],"sourcesContent":["import type { DiagnosticReporter } from '../diagnostic'\n\n/**\n * Creates a reporter that POSTs each diagnostic as JSON to the given URL.\n * Errors are swallowed so reporting never throws into user code.\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function createFetchReporter(url: string): DiagnosticReporter {\n return (diagnostic) => {\n fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(diagnostic),\n }).catch(() => {})\n }\n}\n"],"mappings":";;;;;;AAOA,SAAgB,oBAAoB,KAAiC;CACnE,QAAQ,eAAe;EACrB,MAAM,KAAK;GACT,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAC/C,MAAM,KAAK,UAAU,WAAW;GACjC,CAAC,CAAC,YAAY,GAAG"}
{"version":3,"file":"fetch.mjs","names":[],"sources":["../../src/reporters/fetch.ts"],"sourcesContent":["import type { DiagnosticReporter } from '../diagnostic'\n\n/**\n * Creates a reporter that POSTs each diagnostic as JSON to the given URL.\n * Errors are swallowed so reporting never throws into user code.\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function createFetchReporter(url: string): DiagnosticReporter {\n return (diagnostic) => {\n fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(diagnostic),\n }).catch(() => {})\n }\n}\n"],"mappings":";;;;;;AAOA,SAAgB,oBAAoB,KAAiC;CACnE,QAAQ,eAAe;EACrB,MAAM,KAAK;GACT,QAAQ;GACR,SAAS,EAAE,gBAAgB,mBAAmB;GAC9C,MAAM,KAAK,UAAU,UAAU;EACjC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;CACnB;AACF"}

@@ -1,2 +0,2 @@

import { l as DiagnosticReporter } from "../diagnostic-6-n-OsUx.mjs";
import { l as DiagnosticReporter } from "../diagnostic-ChOW5Avm.mjs";

@@ -3,0 +3,0 @@ //#region src/reporters/node.d.ts

@@ -1,1 +0,1 @@

{"version":3,"file":"node.mjs","names":[],"sources":["../../src/reporters/node.ts"],"sourcesContent":["import type { Diagnostic, DiagnosticReporter } from '../diagnostic'\nimport { appendFileSync } from 'node:fs'\n\nexport interface FileReporterOptions {\n /**\n * Path to the log file.\n * @default '.nostics.log'\n */\n logFile?: string\n\n /**\n * Stack frames matching ANY of these patterns are removed from\n * `diagnostic.stack` before it is written to the log file. Useful to strip\n * `node_modules` and Node internals. The `Error: ...` header line is\n * always preserved.\n */\n excludeStackFrames?: readonly RegExp[]\n}\n\nfunction applyExcludeStackFrames(raw: string, exclude: readonly RegExp[]): string {\n const [header, ...frames] = raw.split('\\n')\n return [header, ...frames.filter(frame => !exclude.some(re => re.test(frame)))].join('\\n')\n}\n\n/**\n * Creates a reporter that appends diagnostics as NDJSON to a local file.\n * Each diagnostic is written as a single JSON line. The diagnostic's `stack`\n * (if present) is included in the payload; noisy frames can be removed via\n * {@link FileReporterOptions.excludeStackFrames}.\n *\n * @example\n * ```ts\n * import { defineDiagnostics } from 'nostics'\n * import { createFileReporter } from 'nostics/reporters/node'\n *\n * const diagnostics = defineDiagnostics({\n * codes: { ... },\n * reporters: [createFileReporter({\n * excludeStackFrames: [/\\/node_modules\\//, /\\(node:/],\n * })],\n * })\n * ```\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function createFileReporter(options?: FileReporterOptions): DiagnosticReporter {\n const logFile = options?.logFile ?? '.nostics.log'\n const excludeStackFrames = options?.excludeStackFrames\n return (diagnostic) => {\n try {\n const d = diagnostic as Diagnostic & Record<string, unknown>\n const base: Record<string, unknown>\n = typeof d.toJSON === 'function' ? (d.toJSON() as Record<string, unknown>) : { ...d }\n if (d.stack) {\n base.stack = excludeStackFrames?.length\n ? applyExcludeStackFrames(d.stack, excludeStackFrames)\n : d.stack\n }\n appendFileSync(logFile, `${JSON.stringify(base)}\\n`)\n }\n catch (err: unknown) {\n console.error(`[nostics]: Failed to write log to \"${logFile}\":`, err)\n }\n }\n}\n"],"mappings":";;AAmBA,SAAS,wBAAwB,KAAa,SAAoC;CAChF,MAAM,CAAC,QAAQ,GAAG,UAAU,IAAI,MAAM,KAAK;CAC3C,OAAO,CAAC,QAAQ,GAAG,OAAO,QAAO,UAAS,CAAC,QAAQ,MAAK,OAAM,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK;;;;;;;;;;;;;;;;;;;;;;AAuB5F,SAAgB,mBAAmB,SAAmD;CACpF,MAAM,UAAU,SAAS,WAAW;CACpC,MAAM,qBAAqB,SAAS;CACpC,QAAQ,eAAe;EACrB,IAAI;GACF,MAAM,IAAI;GACV,MAAM,OACF,OAAO,EAAE,WAAW,aAAc,EAAE,QAAQ,GAA+B,EAAE,GAAG,GAAG;GACvF,IAAI,EAAE,OACJ,KAAK,QAAQ,oBAAoB,SAC7B,wBAAwB,EAAE,OAAO,mBAAmB,GACpD,EAAE;GAER,eAAe,SAAS,GAAG,KAAK,UAAU,KAAK,CAAC,IAAI;WAE/C,KAAc;GACnB,QAAQ,MAAM,sCAAsC,QAAQ,KAAK,IAAI"}
{"version":3,"file":"node.mjs","names":[],"sources":["../../src/reporters/node.ts"],"sourcesContent":["import type { Diagnostic, DiagnosticReporter } from '../diagnostic'\nimport { appendFileSync } from 'node:fs'\n\nexport interface FileReporterOptions {\n /**\n * Path to the log file.\n * @default '.nostics.log'\n */\n logFile?: string\n\n /**\n * Stack frames matching ANY of these patterns are removed from\n * `diagnostic.stack` before it is written to the log file. Useful to strip\n * `node_modules` and Node internals. The `Error: ...` header line is\n * always preserved.\n */\n excludeStackFrames?: readonly RegExp[]\n}\n\nfunction applyExcludeStackFrames(raw: string, exclude: readonly RegExp[]): string {\n const [header, ...frames] = raw.split('\\n')\n return [header, ...frames.filter(frame => !exclude.some(re => re.test(frame)))].join('\\n')\n}\n\n/**\n * Creates a reporter that appends diagnostics as NDJSON to a local file.\n * Each diagnostic is written as a single JSON line. The diagnostic's `stack`\n * (if present) is included in the payload; noisy frames can be removed via\n * {@link FileReporterOptions.excludeStackFrames}.\n *\n * @example\n * ```ts\n * import { defineDiagnostics } from 'nostics'\n * import { createFileReporter } from 'nostics/reporters/node'\n *\n * const diagnostics = defineDiagnostics({\n * codes: { ... },\n * reporters: [createFileReporter({\n * excludeStackFrames: [/\\/node_modules\\//, /\\(node:/],\n * })],\n * })\n * ```\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function createFileReporter(options?: FileReporterOptions): DiagnosticReporter {\n const logFile = options?.logFile ?? '.nostics.log'\n const excludeStackFrames = options?.excludeStackFrames\n return (diagnostic) => {\n try {\n const d = diagnostic as Diagnostic & Record<string, unknown>\n const base: Record<string, unknown>\n = typeof d.toJSON === 'function' ? (d.toJSON() as Record<string, unknown>) : { ...d }\n if (d.stack) {\n base.stack = excludeStackFrames?.length\n ? applyExcludeStackFrames(d.stack, excludeStackFrames)\n : d.stack\n }\n appendFileSync(logFile, `${JSON.stringify(base)}\\n`)\n }\n catch (err: unknown) {\n console.error(`[nostics]: Failed to write log to \"${logFile}\":`, err)\n }\n }\n}\n"],"mappings":";;AAmBA,SAAS,wBAAwB,KAAa,SAAoC;CAChF,MAAM,CAAC,QAAQ,GAAG,UAAU,IAAI,MAAM,IAAI;CAC1C,OAAO,CAAC,QAAQ,GAAG,OAAO,QAAO,UAAS,CAAC,QAAQ,MAAK,OAAM,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;AAC3F;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,mBAAmB,SAAmD;CACpF,MAAM,UAAU,SAAS,WAAW;CACpC,MAAM,qBAAqB,SAAS;CACpC,QAAQ,eAAe;EACrB,IAAI;GACF,MAAM,IAAI;GACV,MAAM,OACF,OAAO,EAAE,WAAW,aAAc,EAAE,OAAO,IAAgC,EAAE,GAAG,EAAE;GACtF,IAAI,EAAE,OACJ,KAAK,QAAQ,oBAAoB,SAC7B,wBAAwB,EAAE,OAAO,kBAAkB,IACnD,EAAE;GAER,eAAe,SAAS,GAAG,KAAK,UAAU,IAAI,EAAE,GAAG;EACrD,SACO,KAAc;GACnB,QAAQ,MAAM,sCAAsC,QAAQ,KAAK,GAAG;EACtE;CACF;AACF"}
{
"name": "nostics",
"type": "module",
"version": "0.5.0",
"version": "1.0.0",
"description": "Structured diagnostic code library",

@@ -38,20 +38,20 @@ "author": "Anthony Fu <anthonyfu117@hotmail.com>",

"@posva/prompts": "^2.4.4",
"@types/node": "^25.7.0",
"@types/node": "^25.9.3",
"@types/semver": "^7.7.1",
"@vitest/coverage-v8": "4.1.6",
"@vitest/ui": "4.1.6",
"@vitest/coverage-v8": "4.1.8",
"@vitest/ui": "4.1.8",
"conventional-changelog": "^7.2.0",
"conventional-changelog-angular": "^8.3.1",
"eslint": "^10.3.0",
"lint-staged": "^17.0.4",
"oxfmt": "^0.50.0",
"eslint": "^10.5.0",
"lint-staged": "^17.0.7",
"oxfmt": "^0.54.0",
"publint": "^0.3.21",
"semver": "^7.8.0",
"semver": "^7.8.4",
"simple-git-hooks": "^2.13.1",
"tsdown": "^0.22.0",
"tsdown": "^0.22.2",
"tsnapi": "^0.3.3",
"typescript": "^6.0.3",
"vite": "^8.0.12",
"vitest": "^4.1.6",
"nostics": "0.5.0"
"vite": "^8.0.16",
"vitest": "^4.1.8",
"nostics": "1.0.0"
},

@@ -58,0 +58,0 @@ "simple-git-hooks": {

@@ -6,2 +6,3 @@ ---

allowed-tools: Read Grep Glob Edit Write
license: MIT
---

@@ -11,46 +12,19 @@

## Step 1: Find the Target File
1. **Find the catalog.** Grep for `defineDiagnostics` to locate the `codes` object the new entry belongs in. With several catalogs, pick by area.
2. **Pick the code.** `PREFIX_XNNNN`: `PREFIX` is the project name uppercased (`NUXT`, `I18N`); `X` is the area letter (`B` build, `R` runtime, `C` config, `D` deprecation); `NNNN` is the next free number. Read existing codes to choose. Never rename or reuse a published code.
3. **Add the entry:**
Locate the file containing the `defineDiagnostics()` call where the new code should be added.
Use Grep to search for `defineDiagnostics` across the project.
## Step 2: Determine the Code Identifier
Diagnostic codes follow the pattern `PREFIX_XNNNN`:
- **PREFIX**: project/domain name in uppercase (e.g., `NUXT`, `MATH`, `I18N`)
- **X**: category letter. `B` (build), `R` (runtime), `C` (config), `E` (error), `W` (warning), `D` (deprecation), `I` (info)
- **NNNN**: numeric sequence
Check existing codes in the file to pick the prefix, category, and next free number. Never reuse a published code.
## Step 3: Add the Definition
Add the new entry to the `codes` object inside `defineDiagnostics()`:
```ts
CODE_NAME: {
why: 'Static explanation of why this failed.',
// OR with parameters:
why: (p: { paramName: string }) => `Template with ${p.paramName}.`,
fix: 'How to resolve the issue.', // optional but recommended
docs: 'https://example.com/custom-page', // optional — overrides docsBase, or `false` to opt out
LIB_R0001: {
why: (p: { hook: string }) => `${p.hook}() must run at the top of setup().`, // string or typed fn; becomes Error.message (required)
fix: 'Move the call into setup() or a composable it calls.', // optional, but add whenever the fix is known
docs: 'https://example.com/custom', // optional: overrides docsBase, or `false` to opt out
},
```
Rules:
- `why` is the only required field. Params from `why` and `fix` are intersected and required at the call site.
- Runtime fields (`cause`, `sources`) are passed at the call site, never in the definition.
- `why` is the only required field. It becomes `Error.message` on the resulting `Diagnostic` instance.
- Parameters can appear in any template field (`why`, `fix`). TypeScript unions them and requires them at the call site.
- Always provide `fix` when the solution is known
- Use typed arrow functions for parameterized templates: `(p: { key: Type }) => string`
- Runtime fields (`cause`, `sources`) are passed at the call site, not in the definition
- `docs?: string | false` overrides `docsBase` for this code, or opts out entirely with `false`
4. **Call it:** `diagnostics.LIB_R0001({ hook })` to report, `throw diagnostics.LIB_R0001({ hook, cause: err })` to raise. Both run the reporters.
## Step 4: Call the Code
```ts
diagnostics.CODE_NAME({ paramName: 'value' })
throw diagnostics.CODE_NAME({ paramName: 'value', cause: originalError })
```
Full API and reporter/formatter/plugin details: the `nostics` skill.
# Documentation Site and Error Code Registry
## Table of Contents
Every published code needs a stable, public documentation page forever. It serves three audiences: **developers** clicking the `see:` URL from their terminal, **AI agents** fetching the page to help when a user pastes an error, and **search engines** indexing `NUXT_B2011` so it's findable. Plan the URL structure to match `docsBase` (string form → `${docsBase}/${code.toLowerCase()}`; function form → full control).
- [Why a documentation site matters](#why-a-documentation-site-matters)
- [Setting up docsBase](#setting-up-docsbase)
- [Documentation page structure](#documentation-page-structure)
- [Page template](#page-template)
- [Deployment recommendations](#deployment-recommendations)
- [Keeping docs in sync with code](#keeping-docs-in-sync-with-code)
- [Optimizing for AI agent consumption](#optimizing-for-ai-agent-consumption)
## Why a documentation site matters
Every diagnostic code needs a public documentation page. It serves three audiences:
1. **Developers** encountering the error in their terminal or logs can click the `see:` URL and get immediate guidance
2. **AI agents** (Claude, Copilot, etc.) can fetch the page to help when a user pastes an error
3. **Search engines** index these pages so developers searching for `NUXT_B2011` find the answer directly
## Setting up docsBase
The `docsBase` option in `defineDiagnostics()` controls the auto-generated `docs` URL. It can be a string or a function:
```ts
// Function form — full control over the URL
const diagnostics = defineDiagnostics({
docsBase: (code) => `https://nuxt.com/e/${code.replace('NUXT_', '').toLowerCase()}`,
codes: {
NUXT_B2011: { why: '...' },
},
})
// diagnostics.NUXT_B2011().docs → 'https://nuxt.com/e/b2011'
```
```ts
// String form — code appended automatically as ${docsBase}/${code.toLowerCase()}
const diagnostics = defineDiagnostics({
docsBase: 'https://example.com/errors',
codes: {
MY_E001: { why: '...' },
},
})
// diagnostics.MY_E001().docs → 'https://example.com/errors/my_e001'
```
Plan your URL structure accordingly.
## Documentation page structure
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.
### Required sections
**Title and code identifier**
```markdown
# NUXT_B2011: Invalid plugin — src option is required
Code: `NUXT_B2011`
Level: error
```
Start with the code and a short title. Include the code and the severity level.
**What this error means**
```markdown
## What this error means
This error occurs when a plugin is registered via `addPlugin()` without providing a
valid `src` path. Nuxt requires every plugin to have a source file so it can be
resolved and included in the build.
```
Explain the error in plain language. Assume the reader has no prior context. Describe what the system expected versus what it received. AI agents rely on this section to explain the error to users.
**Why this happens**
```markdown
## Why this happens
Common causes:
- Passing an object to `addPlugin()` without a `src` property
- Passing `undefined` or `null` as the plugin path
- A module is constructing a plugin object dynamically and the `src` field is missing
due to a conditional branch or typo
```
List the concrete scenarios that trigger this diagnostic as bullets. Each bullet should describe a specific situation the developer might be in.
**How to fix it**
````markdown
## How to fix it
Ensure every call to `addPlugin()` includes a valid `src` path:
```ts
// Wrong
addPlugin({ name: 'my-plugin' })
// Correct
addPlugin({ src: resolve('./runtime/my-plugin'), name: 'my-plugin' })
// Also correct — pass a string directly
addPlugin(resolve('./runtime/my-plugin'))
```
If the plugin path is computed dynamically, verify the variable is defined before
passing it to `addPlugin()`.
````
Provide concrete code examples showing the wrong pattern and the corrected version. This is the most important section; it should be copy-pasteable.
### Optional sections
**Additional context**
```markdown
## Additional context
- This validation was added in Nuxt 3.2
- If you are writing a Nuxt module, see the [Module Author Guide](https://nuxt.com/docs/guide/going-further/modules)
- Related codes: [NUXT_B1001](./nuxt_b1001) (template compilation), [NUXT_B3005](./nuxt_b3005) (module resolution)
```
Link to related documentation, changelog entries, or related diagnostic codes.
**Example diagnostic output**
````markdown
## Example output
```
[NUXT_B2011] Invalid plugin`/plugins/bad.ts`. src option is required.
├▶ why: plugin object was passed without a src path
├▶ fix: Pass a string path or an object with a `src`property to`addPlugin()`.
├▶ hint: Check your module's addPlugin() calls
╰▶ see: https://nuxt.com/e/b2011
```
````
Show what the user actually sees in their terminal so they can confirm they're on the right page.
## Page template
Use this template for each error code page:
Each code page (`https://nuxt.com/e/b2011`) follows this structure. Keep it human-readable and agent-parseable: consistent `##` headings, actionable content early, no critical info hidden in tabs/collapsed sections/JS-rendered content.

@@ -159,46 +17,31 @@ ```markdown

{Plain-language explanation of the diagnostic. 1-3 sentences.}
{Plain-language explanation, no assumed context: what the system expected vs received. 1-3 sentences. Agents rely on this to explain the error.}
## Why this happens
{Bulleted list of concrete scenarios that trigger this diagnostic.}
{Bulleted list of the concrete scenarios that trigger this diagnostic.}
## How to fix it
{Code examples showing the wrong pattern and the corrected version.}
{The most important section: copy-pasteable code showing the wrong pattern and the corrected version.}
## Additional context
{Links to related docs, changelog, or related diagnostic codes. Optional.}
{Optional: links to related docs, changelog, or related codes.}
## Example output
{Terminal output showing the formatted diagnostic. Optional.}
{Optional: the formatted terminal output, so users confirm they're on the right page.}
```
## Deployment recommendations
## Deployment
Host the error code pages on a public URL that matches your `docsBase`:
- Host on a public URL matching `docsBase`: a static site generator (VitePress, Nuxt Content) with a catch-all `/e/[code]` route, or a dedicated `/errors` route in existing docs.
- Return 200 for valid codes, 404 for unknown ones, so agents and crawlers distinguish them.
- Add frontmatter/`<meta>` structured data (code, level, title). Keep pages lightweight; avoid SPAs that block fetch-based agents.
- **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`
- **Dedicated `/errors` or `/e` route** in your existing documentation site
- Return proper HTTP status codes (200 for valid codes, 404 for unknown ones) so agents and crawlers can tell valid codes from missing ones
- Add `<meta>` tags or frontmatter with structured data (code, level, title) to help agents and search engines parse pages
- Keep pages lightweight. Avoid heavy JavaScript or SPAs that block content rendering for fetch-based agents.
## Keep docs in sync with code
## Keeping docs in sync with code
- Store documentation markdown alongside your diagnostic definitions or in a dedicated `docs/errors/` directory
- Generate an index page listing all codes with their messages and levels
- In CI, validate that every code in `defineDiagnostics()` has a corresponding documentation page. Fail the build if a page is missing.
- When adding a new diagnostic code, add the documentation page in the same PR
## Optimizing for AI agent consumption
Structure pages so an AI agent fetching the URL can extract the right information without ambiguity:
- Use consistent heading hierarchy (`##` for sections)
- Put the most actionable content (fix instructions) early
- Avoid hiding critical information in collapsed sections, tabs, or JavaScript-rendered content
- Include the code in the page title and body so keyword matching works
- Keep code examples self-contained: an agent should be able to suggest the fix from the page content alone.
- Store the markdown alongside the diagnostic definitions or in `docs/errors/`, and add the page in the same PR as a new code.
- Generate an index page listing all codes with messages and levels.
- In CI, validate that every code in `defineDiagnostics()` has a corresponding page; fail the build if one is missing.
---
name: nostics
description: 'Structured diagnostic code library for JavaScript/TypeScript. Turns errors and other conditions into typed, machine-readable `Diagnostic` instances with stable codes, docs URLs, and actionable fields. Use this skill whenever the project imports `nostics`, or works with `defineDiagnostics`, the `Diagnostic` class, diagnostic code registries, or structured error handling. Also covers reporters (`createConsoleReporter`, `createFetchReporter` from nostics/reporters/fetch, `createFileReporter` from nostics/reporters/node, `createDevReporter` from nostics/reporters/dev), formatters (`formatDiagnostic`, `ansiFormatter`, `jsonFormatter`), and Vite plugins (`nosticsStrip` from @nostics/unplugin/strip-transform, `nosticsCollector` from @nostics/unplugin/dev-server-collector).'
description: "Structured diagnostic code library for JavaScript/TypeScript. Turns errors and other conditions into typed, machine-readable `Diagnostic` instances with stable codes, docs URLs, and actionable fields. Use this skill whenever the project imports `nostics`, or works with `defineDiagnostics`, the `Diagnostic` class, diagnostic code registries, or structured error handling. Also covers reporters (`createConsoleReporter`, `createFetchReporter` from nostics/reporters/fetch, `createFileReporter` from nostics/reporters/node, `createDevReporter` from nostics/reporters/dev), formatters (`formatDiagnostic`, `ansiFormatter`, `jsonFormatter`), and Vite plugins (`nosticsStrip` from @nostics/unplugin/strip-transform, `nosticsCollector` from @nostics/unplugin/dev-server-collector). Also use when migrating a library's existing `console.warn`/`console.error`/`warn()` helpers or thrown `Error`s to diagnostics: follow `references/migration.md`."
license: MIT
---

@@ -8,37 +9,16 @@

Structured diagnostic code library for JavaScript/TypeScript. Every error condition becomes a typed `Diagnostic` (extending `Error`) with a stable code, docs URL, and actionable fields. Serializable via `toJSON()`.
Every error condition becomes a typed `Diagnostic` (extends `Error`) with a stable code, docs URL, and actionable `fix`. Serializable via `toJSON()`.
## Core Concepts
`Diagnostic`: `name` (the code), `message`/`why` (interpolated text), `fix?`, `docs?`, `sources?` (`'file:line:column'`), `cause?`, `toJSON()`. Throw it, catch it with `instanceof Diagnostic`, send `toJSON()` across process boundaries.
### Diagnostic class
## defineDiagnostics
The fundamental unit is a `Diagnostic` instance. It extends `Error`, so you can throw it, catch it with `instanceof`, and inspect it like any other error. Use `.toJSON()` to send it across process boundaries.
Returns one callable handle per code. Calling a handle builds a fresh `Diagnostic`, fires every reporter in order, and returns it. `throw` the return value to raise (reporters still run, so a thrown diagnostic also reports).
```ts
class Diagnostic extends Error {
name: string // the code, e.g. 'NUXT_B2011'
message: string // human-readable, already interpolated
get why(): string // alias for `message`
fix?: string // how to resolve it
docs?: string // URL to documentation page for this code
sources?: string[] // 'file:line:column' strings contributed by the call site
cause?: unknown // original error if passed at call time
toJSON(): object // plain object with name/why/fix/docs/sources/cause
}
```
### Handles
`defineDiagnostics()` returns one handle per code. Each handle is a plain callable that returns a `Diagnostic` instance: call it to report, `throw` the returned value to raise. Reporters are wired in at definition time and fire on every call.
## API Reference
### `defineDiagnostics(options)`: Define diagnostic codes
```ts
import { createConsoleReporter, defineDiagnostics } from 'nostics'
const diagnostics = defineDiagnostics({
const diagnostics = /*#__PURE__*/ defineDiagnostics({
docsBase: (code) => `https://nuxt.com/e/${code.replace('NUXT_', '').toLowerCase()}`,
reporters: [createConsoleReporter()],
reporters: [/*#__PURE__*/ createConsoleReporter()],
codes: {

@@ -51,8 +31,5 @@ NUXT_B1001: {

why: (p: { src: string }) => `Invalid plugin "${p.src}". src option is required.`,
fix: 'Pass a string path or an object with a `src` property to `addPlugin()`.',
fix: 'Pass a string path or an object with a `src` to `addPlugin()`.',
},
NUXT_B5001: {
why: 'Missing compatibilityDate in nuxt.config.',
fix: (p: { date: string }) => `Add "compatibilityDate: '${p.date}'" to your nuxt.config.`,
},
NUXT_W9001: { why: 'message', docs: false }, // per-code: string overrides docsBase, false opts out
},

@@ -62,89 +39,52 @@ })

**Options:**
- **`docsBase`** `string | (code) => string | undefined`: string appends `/${code.toLowerCase()}`; function returns the full URL (or `undefined` to omit).
- **`codes`**: each definition needs `why` (`string | (params) => string`, the only required field, becomes `Error.message`); optional `fix` (`string | (params) => string`) and `docs` (`string | false`).
- **`reporters`**: fired on every call. Their `options` types are intersected; required reporter options become required at the call site.
- **Param inference**: params from `why` and `fix` are intersected and required at the call site. If `why` needs `{ src }` and `fix` needs `{ date }`, the call requires `{ src, date }`.
| Field | Type | Description |
| ----------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `docsBase` | `string \| ((code) => string \| undefined)?` | Docs URL source. String: auto-generates `docs` as `${docsBase}/${code.toLowerCase()}`. Function: receives the code key, returns the full URL (or `undefined` to omit). |
| `codes` | `Record<string, DiagnosticDefinition>` | Map of code keys to their definitions. |
| `reporters` | `readonly DiagnosticReporter[]?` | Reporters fired on every handle call. Their options are inferred and intersected; required reporter options become required at the call site. |
## Call sites
**DiagnosticDefinition fields:**
| Field | Type | Description |
| ------ | ------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| `why` | `string \| (params) => string` | Required. Why this failed. Becomes the `Error.message`. |
| `fix` | `string \| (params) => string` | Optional. How to resolve the issue. |
| `docs` | `string \| false` | Optional. Per-code docs URL override. `string` replaces `docsBase` for this code; `false` opts out entirely. |
**Per-code docs override:**
```ts
codes: {
NUXT_B2011: {
why: 'message',
docs: 'https://nuxt.com/custom/b2011', // overrides docsBase for this code
},
NUXT_W9001: {
why: 'message',
docs: false, // opts out — no `see:` line rendered
},
}
diagnostics.NUXT_B1001() // no params: report only
diagnostics.NUXT_B2011({ src: '/plugins/bad.ts' }) // params first
diagnostics.NUXT_B2011({ src, cause: originalError, sources: ['nuxt.config.ts:42:3'] }) // runtime fields merge in
diagnostics.NUXT_B2011({ src }, { method: 'error' }) // reporter options second
throw diagnostics.NUXT_B2011({ src }) // raise
```
**Type inference:** Parameters from all template fields (`why`, `fix`) are intersected. If `why` needs `{ src }` and `fix` needs `{ date }`, the call site requires `{ src, date }`.
`cause`/`sources` go in the params object; `sources` matters most for build/config diagnostics where the JS stack points inside the library. Catch with `if (err instanceof Diagnostic)` then read `.name`, `.message`, `.fix`, `.docs`.
### Calling handles: call sites
## Reporters
```ts
// No params — call signature omits the params arg.
diagnostics.NUXT_B1001()
`(diagnostic: Diagnostic, options?: Opts) => void`. Declaring a required `options` type makes the second call-site argument required and typed.
// With params — first arg is the params object.
diagnostics.NUXT_B2011({ src: '/plugins/bad.ts' })
| Reporter | Import | Description |
| --------------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `createConsoleReporter(options?)` | `nostics` | `console[method](formatter(d))`. `method` defaults `'warn'` (`'log'\|'warn'\|'error'`), `formatter` defaults `formatDiagnostic`; both via options, `method` also overridable per call. |
| `createFetchReporter(url)` | `nostics/reporters/fetch` | POSTs diagnostic JSON to the URL; failures swallowed. |
| `createFileReporter(options?)` | `nostics/reporters/node` | Appends NDJSON to a local file (default `.nostics.log`). |
| `createDevReporter()` | `nostics/reporters/dev` | Sends `toJSON()` to the Vite dev server via `import.meta.hot.send()`. |
// Runtime call-site fields (`cause`, `sources`) merge into the same object.
diagnostics.NUXT_B2011({
src: pluginPath,
cause: originalError,
sources: ['nuxt.config.ts:42:3'],
})
// To raise instead of just reporting, `throw` the returned diagnostic.
throw diagnostics.NUXT_B2011({ src: pluginPath })
```
The handle returns the `Diagnostic` and fires every reporter in order. `throw` the return value to raise.
### Catching diagnostics
`Diagnostic` extends `Error`, so it behaves like any other thrown error.
```ts
import { Diagnostic } from 'nostics'
try {
throw diagnostics.NUXT_B2011({ src: pluginPath })
} catch (err) {
if (err instanceof Diagnostic) {
console.log(err.name) // 'NUXT_B2011'
console.log(err.message) // already-interpolated text
console.log(err.docs) // 'https://nuxt.com/e/b2011'
console.log(err.fix) // 'Pass a string path...'
}
}
import type { DiagnosticReporter } from 'nostics'
const sentryReporter: DiagnosticReporter = (d) =>
sentry.captureMessage(d.message, { tags: { code: d.name } })
const audited: DiagnosticReporter<{ priority: number }> = (d, o) =>
audit.log({ name: d.name, priority: o.priority })
// → audited makes diagnostics.X({...}, { priority: 1 }) required and type-checked.
```
### Formatters
## Formatters
| Formatter | Import | Description |
| ----------------------- | ------------------------- | --------------------------------------------------------------- |
| `formatDiagnostic` | `nostics` | Plain unicode-decorated string. Used by the built-in reporters. |
| `ansiFormatter(colors)` | `nostics/formatters/ansi` | Colorized variant. Accepts a generic `Colors` interface. |
| `jsonFormatter` | `nostics/formatters/json` | `JSON.stringify(diagnostic)` (calls `Diagnostic.toJSON()`). |
| Formatter | Import | Description |
| ----------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `formatDiagnostic` | `nostics` | Plain unicode-decorated string (built-in reporters use it). |
| `ansiFormatter(colors)` | `nostics/formatters/ansi` | Colorized; accepts a `Colors` interface (`red`/`yellow`/`cyan`/`gray`/`bold`/`dim`, each `(s) => string`). |
| `jsonFormatter` | `nostics/formatters/json` | `JSON.stringify(diagnostic)` via `toJSON()`. |
**`formatDiagnostic` output:**
`formatDiagnostic` output, detail order fixed `fix` → `sources` → `see`, missing fields omitted:
```
[NUXT_B2011] Invalid plugin `/plugins/bad.ts`. src option is required.
├▶ fix: Pass a string path or an object with a `src` property to `addPlugin()`.
├▶ fix: Pass a string path or an object with a `src` to `addPlugin()`.
├▶ sources: nuxt.config.ts:42:3

@@ -154,109 +94,21 @@ ╰▶ see: https://nuxt.com/e/b2011

Detail line order is fixed: `fix` → `sources` → `see` (docs URL). Missing fields are omitted.
## Vite plugins (`@nostics/unplugin`, dev dependency)
**ANSI formatter `Colors` interface:**
`@nostics/unplugin/strip-transform` (library authors, build optimization) and `@nostics/unplugin/dev-server-collector` (app developers, dev-time collection). Both unplugin-based: `.vite()`, `.webpack()`, `.rollup()`, etc.
```ts
interface Colors {
red: (s: string) => string
yellow: (s: string) => string
cyan: (s: string) => string
gray: (s: string) => string
bold: (s: string) => string
dim: (s: string) => string
}
```
- **`nosticsStrip`** marks `defineDiagnostics()` `/*#__PURE__*/` and wraps bare diagnostic expression statements with a `NODE_ENV` guard so they tree-shake out of production. Option `packageName?` (default `'nostics'`). Throws/returns/assignments stay (they are behavior). For tracking: relative imports, export the catalog directly, no factory wrappers or deep barrels.
- The plugin is optional. The same production output happens with no build transform if the catalog is annotated by hand: put `/*#__PURE__*/` before `defineDiagnostics(` and before each reporter factory call inside it (as in every example here), and dev-guard each report-only call site (`process.env.NODE_ENV !== 'production' && diagnostics.CODE(p)`). Always write the annotations in source; reach for the plugin when report-only call sites are unguarded and you want stripping without touching them.
- **`nosticsCollector`** listens for `createDevReporter()` diagnostics over the Vite WebSocket and writes them as NDJSON via `createFileReporter`. Vite-only. Options `logFile?` (default `.nostics.log`), `debug?` (default `!!process.env.DEBUG`).
### Reporters
A reporter is `(diagnostic: Diagnostic, options?: Opts) => void`. `defineDiagnostics` unions every reporter's `options`. The call site must pass them only if some reporter has required fields.
| Reporter | Import | Description |
| --------------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `createConsoleReporter(options?)` | `nostics` | Returns a reporter that prints `console[method](formatter(d))`. `method` defaults to `'warn'` (`'log' \| 'warn' \| 'error'`) and `formatter` to `formatDiagnostic`; both are set via `options`, and `method` can also be overridden per call via `{ method }`. |
| `createFetchReporter(url)` | `nostics/reporters/fetch` | POSTs the diagnostic JSON to the given URL. Fetch failures are swallowed. |
| `createFileReporter(options?)` | `nostics/reporters/node` | Appends diagnostics as NDJSON to a local file (default `.nostics.log`). |
| `createDevReporter()` | `nostics/reporters/dev` | Returns a reporter that sends `diagnostic.toJSON()` to the Vite dev server via `import.meta.hot.send()`. |
**Writing a custom reporter:**
```ts
import type { DiagnosticReporter } from 'nostics'
const sentryReporter: DiagnosticReporter = (diagnostic) => {
sentry.captureMessage(diagnostic.message, { tags: { code: diagnostic.name } })
}
```
To require options at the call site, declare a second parameter:
```ts
const audited: DiagnosticReporter<{ priority: number }> = (d, options) => {
audit.log({ name: d.name, priority: options.priority })
}
// → diagnostics.X({...}, { priority: 1 }) — the second arg is required and type-checked.
```
## Vite Plugins
Two unplugin-based plugins shipped in the separate `@nostics/unplugin` package (install it as a dev dependency): `@nostics/unplugin/strip-transform` (for library authors, build-time optimization) and `@nostics/unplugin/dev-server-collector` (for app developers consuming a nostics-using library, dev-time diagnostic collection).
### `nosticsStrip`: Build-time AST transform
Marks `defineDiagnostics()` calls as `/*#__PURE__*/` and wraps diagnostic usage with a `NODE_ENV` guard, so diagnostics tree-shake out of production builds. Works with `.vite()`, `.webpack()`, `.rollup()`, etc. via unplugin.
```ts
// vite.config.ts
import { nosticsStrip } from '@nostics/unplugin/strip-transform'
export default defineConfig({
plugins: [nosticsStrip.vite()],
})
```
**Options (`NosticsStripOptions`):**
| Field | Type | Description |
| ------------- | --------- | ------------------------------------------------------------- |
| `packageName` | `string?` | The package name to detect imports from. Default: `'nostics'` |
### `nosticsCollector`: Dev server diagnostic collector
Listens for diagnostics from `createDevReporter()` in the browser over the Vite WebSocket, then writes them as NDJSON to a local log file via `createFileReporter`. Vite-only.
```ts
import { nosticsCollector } from '@nostics/unplugin/dev-server-collector'
export default defineConfig({ plugins: [nosticsStrip.vite(), nosticsCollector.vite()] })
export default defineConfig({
plugins: [nosticsCollector.vite()],
})
```
**Options (`NosticsCollectorOptions`):**
| Field | Type | Description |
| --------- | ---------- | ------------------------------------------------------------------- |
| `logFile` | `string?` | Path to the log file. Default: `'.nostics.log'` |
| `debug` | `boolean?` | Enable debug logging for the plugin. Default: `!!process.env.DEBUG` |
### Typical dev setup
Use both plugins together with `createDevReporter()` for full dev-time diagnostic capture:
```ts
// vite.config.ts
import { nosticsCollector } from '@nostics/unplugin/dev-server-collector'
import { nosticsStrip } from '@nostics/unplugin/strip-transform'
export default defineConfig({
plugins: [nosticsStrip.vite(), nosticsCollector.vite()],
})
```
```ts
// src/diagnostics.ts
// src/diagnostics.ts — pair the collector with createDevReporter()
import { createConsoleReporter, defineDiagnostics } from 'nostics'
import { createDevReporter } from 'nostics/reporters/dev'
export const diagnostics = defineDiagnostics({
reporters: [createConsoleReporter(), createDevReporter()],
export const diagnostics = /*#__PURE__*/ defineDiagnostics({
reporters: [/*#__PURE__*/ createConsoleReporter(), /*#__PURE__*/ createDevReporter()],
codes: {

@@ -268,35 +120,12 @@ /* ... */

## Best Practices
## Conventions
### Code naming conventions
- **Codes** are stable, fully-qualified `PREFIX_XNNNN` (`B` build, `R` runtime, `C` config, `D` deprecation). Never reuse or reassign a published code.
- Always provide `why`; provide `fix` whenever the solution is known (the most actionable field for humans and agents). Use parameterized templates for runtime values, not string concatenation outside the factory.
- Pass `cause` when re-raising; pass `sources` when the JS stack doesn't reflect the user's source.
- Split large catalogs by domain (`diagnostics/build.ts`, `runtime.ts`, `config.ts`, re-exported from `index.ts`), each `defineDiagnostics()` sharing `docsBase` with its own code range.
- Use fully qualified, stable code identifiers (e.g. `NUXT_B1001`, `I18N_I001`)
- Group by domain using a letter prefix within the code: `B` for build, `R` for runtime, `C` for config, etc.
- Never reuse or reassign a code once published. Codes are permanent identifiers.
## References
### Structuring diagnostic definitions
- Always provide `why`. It is the only required field and becomes `Error.message`.
- Provide `fix` whenever the solution is known. It is the most actionable field for both humans and AI agents.
- Use parameterized templates for messages that include runtime values. Avoid string concatenation outside the factory.
- Pass `cause` at the call site (not in the definition) when re-raising from an original error
- Pass `sources` at the call site as `'file:line:column'` strings when the JS stack trace doesn't reflect the user's source
### Organizing diagnostic files
For large projects, split diagnostics by domain:
```
src/
diagnostics/
build.ts # NUXT_B-series codes
runtime.ts # NUXT_R-series codes
config.ts # NUXT_C-series codes
index.ts # re-exports each set
```
Each file calls `defineDiagnostics()` with the same `docsBase` but different code ranges.
## Documentation Site
For guidance on building error code documentation pages (structure, templates, deployment, and AI-agent optimization), read `references/documentation-site.md`.
- **Migrating an existing library** to nostics (replacing `console.warn`/`console.error`/`warn()`/thrown `Error`s with diagnostic codes, without changing runtime behavior): follow `references/migration.md` start to finish.
- Building the error-code documentation site (page template, deployment, agent optimization): `references/documentation-site.md`.
//#region src/utils.d.ts
/**
* A value of type T, or a function that resolves T from a single params object.
*
* @internal
*/
type ValueOrFn<T, P = any> = T | ((params: P) => T);
/**
* Extracts the param type from a single-arg function, or `never` for
* non-function inputs. Pairs with {@link ValueOrFn}.
*
* @internal
*/
type ExtractFnParam<T> = T extends ((params: infer P) => any) ? P : never;
/**
* Converts a union of types to their intersection.
*
* @internal
*/
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
/**
* `true` when `T` is the `any` type.
*
* @internal
*/
type IsAny<Type> = 0 extends 1 & Type ? true : false;
/**
* `true` when `T` is the `unknown` type (and not `any`).
*
* @internal
*/
type IsUnknown<Type> = IsAny<Type> extends true ? false : unknown extends Type ? true : false;
/**
* Expands a type to its property listing so editor hovers show the resolved
* shape instead of a chain of aliases / intersections.
*
* @internal
*/
type Prettify<Type> = { [Key in keyof Type]: Type[Key] };
//#endregion
//#region src/diagnostic.d.ts
/**
* Define-time shape of a diagnostic. Each field can be a static value or a
* function that resolves it from a shared `params` object passed at call
* time. Runtime-only fields (`cause`, `sources`) from {@link DiagnosticInit}
* are intentionally omitted: they're only meaningful at the call site.
*/
interface DiagnosticDefinition<P = any> {
/**
* The error message: why this failed. String, or a function of `params`.
*
* @example
* ```ts
* why: (p: { name: string }) => `module "${p.name}" failed to load`
* ```
*/
why: ValueOrFn<string, P>;
/**
* Actionable instructions on how to resolve the problem. String, or a
* function of `params`.
*
* @example
* ```ts
* fix: (p: { name: string }) => `run "npm install ${p.name}"`
* ```
*/
fix?: ValueOrFn<string, P>;
/**
* Per-code docs URL. A string overrides
* {@link DefineDiagnosticsOptions.docsBase} for this code; `false` opts this
* code out entirely, even when `docsBase` is set. When omitted, the URL is
* derived from `docsBase`.
*/
docs?: string | false;
}
/**
* Runtime-only fields that can be passed alongside the interpolation params
* at call time. Merged into the same object so callers pass everything in
* one place.
*/
interface DiagnosticCallParams {
/**
* Original error or exception that triggered this diagnostic. Pass it
* through when re-throwing so the original stack trace is preserved.
*/
cause?: unknown;
/**
* Locations in user code that contributed to this diagnostic, in
* `file:line:column` format. Useful for compilers and other tools where the
* JS stack trace doesn't reflect the user's source.
*/
sources?: string[];
}
/**
* Structured initializer for a {@link Diagnostic}. `why` is the only required
* field: it becomes the {@link Diagnostic.message}. The remaining fields are
* optional metadata that reporters and consumers can render or forward.
*/
interface DiagnosticInit extends DiagnosticCallParams {
/**
* The actual error message: why this failed.
* Mirrored to `Error.message`.
*/
why: string;
/**
* Optional actionable instructions on how to resolve the problem.
*/
fix?: string;
/**
* URL to extended documentation for this diagnostic.
*/
docs?: string;
}
/**
* Represents how to report a diagnostic. Could call `console.log()`, send the
* diagnostic to a server, or something else. Reporters declare the shape of
* options they need via `ReporterOpts`; `defineDiagnostics` intersects every
* reporter's options into a single object passed at the call site.
*/
type DiagnosticReporter<ReporterOpts extends object = {}> = (diagnostic: Diagnostic, options: ReporterOpts) => void;
/**
* Permissive reporter constraint used internally so reporters with 1 arg,
* required options, or optional options all satisfy the array constraint.
*
* @internal
*/
type AnyDiagnosticReporter = (diagnostic: Diagnostic, options: any) => void;
/**
* The `console` methods a log reporter can route to.
*/
type ConsoleMethod = "log" | "error" | "warn";
/**
* Options for {@link createConsoleReporter}.
*/
interface ConsoleReporterOptions {
/**
* `console` method used to print the diagnostic. Defaults to `'warn'`. The
* returned reporter still accepts a per-call `{ method }` override through
* the call-site reporter options.
*/
method?: ConsoleMethod;
/**
* Renders the diagnostic into the string handed to `console`. Defaults to
* {@link formatDiagnostic}, the plain unicode-decorated formatter.
*/
formatter?: (diagnostic: Diagnostic) => string;
}
/**
* Creates a console reporter that renders each diagnostic with `formatter` and
* prints the result via `console[method]`. Both default sensibly (`'warn'` and
* {@link formatDiagnostic}); `method` can also be overridden per call through
* the reporter options.
*/
declare function createConsoleReporter({
method: defaultMethod,
formatter
}?: ConsoleReporterOptions): DiagnosticReporter<{
method?: ConsoleMethod;
}>;
/**
* Ready-made console reporter, equivalent to `createConsoleReporter()`.
*
* @deprecated Use `createConsoleReporter()` instead.
*/
declare const reporterLog: DiagnosticReporter<{
method?: ConsoleMethod;
}>;
/**
* Resolves the `params` type a code expects from the intersection of params
* across all function-typed fields, falling back to `{}` when every field is
* static. Merged with {@link DiagnosticCallParams} at the call site.
*
* @internal
*/
type InferCodeParams<Def> = [ExtractFnParam<Def[keyof Def]>] extends [never] ? {} : UnionToIntersection<ExtractFnParam<Def[keyof Def]>>;
/**
* Options for {@link defineDiagnostics}.
*/
interface DefineDiagnosticsOptions<Codes extends Record<string, DiagnosticDefinition>, Reporters extends readonly AnyDiagnosticReporter[]> {
/**
* Base URL or resolver for documentation links. When a string, the code is
* appended as a lowercase path segment (e.g. `"https://docs.example.com"` →
* `"https://docs.example.com/math_e001"`). When a function, receives the
* code and returns a URL or `undefined`.
*/
docsBase?: string | ((code: keyof Codes) => string | undefined);
/**
* Map of diagnostic codes to their definitions.
*/
codes: Codes;
/**
* Reporters called every time a diagnostic is produced. Can be used to
* integrate with custom logging.
*/
reporters?: Reporters;
}
/**
* The first positional argument of a {@link DiagnosticHandle} call:
* interpolation params merged with the runtime-only call-site fields
* (`cause`, `sources`).
*
* @internal
*/
type CallSiteParams<Params> = Params & DiagnosticCallParams;
/**
* Resolves the full argument tuple for a {@link DiagnosticHandle} call.
* Branches on whether params and reporter options each have required fields.
* Required positions become required tuple elements, all-optional ones
* become `?`, and when no reporter declares any options the parameter is
* omitted entirely.
*
* @internal
*/
type ActionArgs<Params, ReporterOpts> = keyof ReporterOpts extends never ? {} extends Params ? [params?: CallSiteParams<Params>] : [params: CallSiteParams<Params>] : {} extends ReporterOpts ? {} extends Params ? [params?: CallSiteParams<Params>, reporterOptions?: ReporterOpts] : [params: CallSiteParams<Params>, reporterOptions?: ReporterOpts] : {} extends Params ? [params: CallSiteParams<Params> | undefined, reporterOptions: ReporterOpts] : [params: CallSiteParams<Params>, reporterOptions: ReporterOpts];
/**
* Per-code handle exposed by {@link defineDiagnostics}. Each code is a
* callable: invoke it to build the diagnostic and run every reporter, or
* prefix the call with `throw` to raise it.
*
* @example
* ```ts
* diagnostics.MATH_E001({ name: 'x' }) // report
* throw diagnostics.MATH_E001({ name: 'x' }) // throw
* ```
*/
interface DiagnosticHandle<Params, ReporterOpts> {
/**
* Builds the diagnostic, runs every reporter, and returns the diagnostic
* instance. The returned diagnostic can be inspected, attached as `cause`,
* or thrown with `throw`.
*/
(...args: ActionArgs<Params, ReporterOpts>): Diagnostic;
}
/**
* Return type of {@link defineDiagnostics}.
*/
type Diagnostics<Codes extends Record<string, DiagnosticDefinition>, Reporters extends readonly AnyDiagnosticReporter[]> = { [Code in keyof Codes]: DiagnosticHandle<InferCodeParams<Codes[Code]>, Prettify<ExtractReportersOptions<Reporters>>> };
declare class Diagnostic extends Error {
name: string;
/**
* URL to extended documentation for this diagnostic code.
* Auto-generated from {@link DefineDiagnosticsOptions.docsBase}.
*/
docs?: string;
/**
* Optional actionable instructions on how to resolve the problem.
*/
fix?: string;
/**
* Locations in user code that contributed to this diagnostic, in
* `file:line:column` format.
*/
sources?: string[];
/**
* Alias for {@link Error.message}: the reason this diagnostic was raised.
*/
get why(): string;
/**
* @param init structured initializer; `why` is required
* @param captureFrom V8 stack-cutoff frame. Defaults to {@link Diagnostic}
* so the top of the trace is the `new Diagnostic(...)` call site.
* `defineDiagnostics` passes its action method to strip its own frames too.
* Ignored on engines without `Error.captureStackTrace`.
*/
constructor(init: DiagnosticInit, captureFrom?: Function);
/**
* Converts the diagnostic into a serializable structured object.
*/
toJSON(): object;
}
/**
* Creates a typed diagnostics object from a set of code definitions. Each
* code becomes a callable {@link DiagnosticHandle}: invoke to report, or
* `throw` the result to raise. No `new` required, no proxy.
*/
declare function defineDiagnostics<const Codes extends Record<string, DiagnosticDefinition>, const Reporters extends readonly AnyDiagnosticReporter[]>(options: DefineDiagnosticsOptions<Codes, Reporters>): Diagnostics<Codes, Reporters>;
/**
* Extracts the options object a reporter accepts as its 2nd argument. Returns
* `{}` when the reporter has no 2nd arg (so it contributes nothing to the
* merged shape).
*/
type ExtractSingleReporterOptions<Reporter> = Reporter extends ((diagnostic: Diagnostic, options: infer ReporterOpts) => any) ? IsUnknown<ReporterOpts> extends true ? {} : Exclude<ReporterOpts, undefined> : {};
/**
* Intersects every reporter's options shape into a single object. If any
* reporter has a required field, the merged shape has a required field, and
* {@link ActionArgs} flips `reporterOptions` from optional to required via
* `{} extends Merged`.
*/
type ExtractReportersOptions<Reporters extends readonly any[]> = Reporters extends readonly [infer First, ...infer Rest] ? ExtractSingleReporterOptions<First> & ExtractReportersOptions<Rest> : {};
//#endregion
export { DiagnosticCallParams as a, DiagnosticInit as c, defineDiagnostics as d, reporterLog as f, Diagnostic as i, DiagnosticReporter as l, ConsoleReporterOptions as n, DiagnosticDefinition as o, ValueOrFn as p, DefineDiagnosticsOptions as r, DiagnosticHandle as s, ConsoleMethod as t, createConsoleReporter as u };
//# sourceMappingURL=diagnostic-6-n-OsUx.d.mts.map