nostics 0.0.0 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -0
- package/dist/dev-reporter.d.mts +7 -0
- package/dist/dev-reporter.mjs +9 -0
- package/dist/dev-reporter.mjs.map +1 -0
- package/dist/diagnostics-CSUYJHfP.d.mts +59 -0
- package/dist/format-CKWeokpQ.d.mts +10 -0
- package/dist/format-DYh8khIQ.mjs +21 -0
- package/dist/format-DYh8khIQ.mjs.map +1 -0
- package/dist/formatters/ansi.d.mts +15 -0
- package/dist/formatters/ansi.mjs +30 -0
- package/dist/formatters/ansi.mjs.map +1 -0
- package/dist/formatters/json.d.mts +7 -0
- package/dist/formatters/json.mjs +6 -0
- package/dist/formatters/json.mjs.map +1 -0
- package/dist/index.d.mts +38 -0
- package/dist/index.mjs +192 -0
- package/dist/index.mjs.map +1 -0
- package/dist/reporter-DDQiiD6l.d.mts +9 -0
- package/dist/unplugin.d.mts +28 -0
- package/dist/unplugin.mjs +207 -0
- package/dist/unplugin.mjs.map +1 -0
- package/package.json +84 -7
- package/skills/add-diagnostic/SKILL.md +48 -0
- package/skills/nostics/SKILL.md +297 -0
- package/skills/nostics/references/documentation-site.md +203 -0
- package/index.js +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# nostics
|
|
2
|
+
|
|
3
|
+
Structured diagnostic codes for JavaScript/TypeScript libraries and frameworks.
|
|
4
|
+
|
|
5
|
+
`nostics` makes every error, warning, and suggestion a **structured, typed, machine-readable object** — with a stable code, a human explanation, and enough context for an agent to act on it precisely.
|
|
6
|
+
|
|
7
|
+
## What it looks like
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
[NUXT_B2011] Invalid plugin `/plugins/bad.ts`. src option is required.
|
|
11
|
+
├▶ fix: Pass a string path or an object with a `src` property to `addPlugin()`.
|
|
12
|
+
├▶ hint: Check your module's addPlugin() calls
|
|
13
|
+
╰▶ see: https://nuxt.com/e/b2011
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Every diagnostic has a **stable code**, a **human-readable explanation**, and structured fields — `fix`, `why`, `hint`, `docs` — that tell you what happened and how to resolve it. The `see` link points to a dedicated documentation page for that error code, with detailed explanations, examples, and common solutions. It should be visited only if more context is needed beyond the concise inline message.
|
|
17
|
+
|
|
18
|
+
These error messages are designed to be **actionable** — not just telling you what went wrong, but guiding you toward the fix.
|
|
19
|
+
|
|
20
|
+
## For humans
|
|
21
|
+
|
|
22
|
+
A code like `NUXT_B2011` is stable and searchable — look it up in docs, find it in source, share it in issues. The structured fields give you immediate guidance: you don't need to search for the error message to find the fix, it's right there. And when you need more detail, the docs link takes you to a dedicated page for that specific error code — with explanations and examples to understand the root cause and how to resolve it.
|
|
23
|
+
|
|
24
|
+
When library authors define diagnostics in one place, messages stay consistent. Every occurrence of `NUXT_B2011` produces the same explanation, the same fix, the same docs link.
|
|
25
|
+
|
|
26
|
+
## For AI agents
|
|
27
|
+
|
|
28
|
+
Structured diagnostics enable a different kind of agent integration — **precise rather than probabilistic**.
|
|
29
|
+
|
|
30
|
+
Every diagnostic has a stable code an agent can dispatch on directly, instead of pattern-matching on message text. The object carries everything needed to act: what happened (`message`), why (`why`), how to fix it (`fix`), where to learn more (`docs`), and machine-readable details (`context`, `sources`). The per-code documentation pages are equally useful for agents — they can crawl or fetch them for deeper context when the inline fields aren't enough.
|
|
31
|
+
|
|
32
|
+
An agent can resolve the issue without asking the user for more information. Users don't need to configure anything — if a library uses `nostics`, its diagnostics are already agent-ready. All codes are defined in a catalog, so an agent can enumerate the full error surface ahead of time. And when multiple libraries in a stack adopt it, agents get uniformly structured data from every layer.
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### Define diagnostics
|
|
37
|
+
|
|
38
|
+
`defineDiagnostics()` declares the diagnostic codes for a domain — pure data, no side effects.
|
|
39
|
+
|
|
40
|
+
`docsBase` can be a string (auto-appends `/${code.toLowerCase()}`) or a function for full control over the URL:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { defineDiagnostics } from 'nostics'
|
|
44
|
+
|
|
45
|
+
// Function form — strip the project prefix for cleaner URLs
|
|
46
|
+
const diagnostics = defineDiagnostics({
|
|
47
|
+
docsBase: code => `https://nuxt.com/e/${code.replace('NUXT_', '').toLowerCase()}`,
|
|
48
|
+
codes: {
|
|
49
|
+
NUXT_B2011: {
|
|
50
|
+
message: (p: { src: string }) => `Invalid plugin \`${p.src}\`. src option is required.`,
|
|
51
|
+
fix: 'Pass a string path or an object with a `src` property to `addPlugin()`.',
|
|
52
|
+
},
|
|
53
|
+
NUXT_B5001: {
|
|
54
|
+
message: 'Missing compatibilityDate in nuxt.config.',
|
|
55
|
+
fix: (p: { date: string }) => `Add \`compatibilityDate: '${p.date}'\` to your nuxt.config.`,
|
|
56
|
+
hint: 'This ensures consistent behavior across Nuxt versions.',
|
|
57
|
+
level: 'warn',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
// diagnostics.NUXT_B2011({ src: '...' }).docs → 'https://nuxt.com/e/b2011'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
// String form — simple base URL, code appended automatically
|
|
66
|
+
const diagnostics = defineDiagnostics({
|
|
67
|
+
docsBase: 'https://example.com/errors',
|
|
68
|
+
codes: {
|
|
69
|
+
MY_E001: { message: 'Something went wrong.' },
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
// diagnostics.MY_E001().docs → 'https://example.com/errors/my_e001'
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Create a logger
|
|
76
|
+
|
|
77
|
+
`createLogger()` binds diagnostics to output — formatting and reporting.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { createLogger } from 'nostics'
|
|
81
|
+
|
|
82
|
+
const log = createLogger({
|
|
83
|
+
diagnostics: [diagnostics],
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
log.NUXT_B2011({ src: pluginPath }).throw()
|
|
87
|
+
log.NUXT_B5001({ date: '2025-01-01' }).warn()
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`log.NUXT_B2011` is cmd+clickable, TypeScript enforces params, and actions are chainable — `.throw()`, `.warn()`, `.error()`, `.log()`.
|
|
91
|
+
|
|
92
|
+
### Multiple diagnostic sets
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
const log = createLogger({
|
|
96
|
+
diagnostics: [nuxtDiagnostics, i18nDiagnostics],
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
log.NUXT_B2011({ src: pluginPath }).throw() // [NUXT_B2011] ...
|
|
100
|
+
log.I18N_I001({ locale: 'fr' }).warn() // [I18N_I001] ...
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Custom reporters
|
|
104
|
+
|
|
105
|
+
Pass a single reporter function or an array:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
const log = createLogger({
|
|
109
|
+
diagnostics: [diagnostics],
|
|
110
|
+
reporter: [
|
|
111
|
+
consoleReporter,
|
|
112
|
+
(diagnostic, formatted) => {
|
|
113
|
+
sentry.captureMessage(formatted, { tags: { code: diagnostic.code } })
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
})
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Features
|
|
120
|
+
|
|
121
|
+
- Typed diagnostic factories — cmd+clickable (go to definition), hover shows params, TypeScript enforces correctness
|
|
122
|
+
- Structured `Diagnostic` objects — serializable, transportable across process boundaries
|
|
123
|
+
- Chainable actions — `.throw()`, `.warn()`, `.error()`, `.log()`
|
|
124
|
+
- Pluggable formatters — plain text, ANSI colors, JSON
|
|
125
|
+
- Pluggable reporters — console, HTTP, custom
|
|
126
|
+
- Multiple diagnostic sets — compose diagnostics from different libraries
|
|
127
|
+
- Zero runtime dependencies
|
|
128
|
+
|
|
129
|
+
## Claude Code plugin
|
|
130
|
+
|
|
131
|
+
nostics ships a [Claude Code plugin](https://docs.anthropic.com/en/docs/claude-code/skills) that gives agents full context on the diagnostics API.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# temporary install because private repo, should be a claude code marketplace in the future
|
|
135
|
+
gh api repos/vercel-labs/nostics/contents/install.sh --jq '.content' | base64 -d | bash
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The plugin includes two skills:
|
|
139
|
+
|
|
140
|
+
- **nostics** — auto-triggered reference skill. Loaded automatically when Claude Code detects nostics imports or API usage in your project, providing context on `defineDiagnostics`, `createLogger`, formatters, reporters, and best practices.
|
|
141
|
+
- **`/add-diagnostic`** — user-invocable skill. Guides the agent through adding a new diagnostic code following the `PREFIX_XNNNN` convention, picking the right file, category, and sequence number.
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/dev-reporter.ts
|
|
2
|
+
const devReporter = (diagnostic) => {
|
|
3
|
+
if (import.meta.hot && typeof import.meta.hot.send === "function") import.meta.hot.send("nostics:report", diagnostic);
|
|
4
|
+
else console.warn("[nostics]: import.meta.hot.send() is not available. This must be running on Vite.");
|
|
5
|
+
};
|
|
6
|
+
//#endregion
|
|
7
|
+
export { devReporter };
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=dev-reporter.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-reporter.mjs","names":[],"sources":["../src/dev-reporter.ts"],"sourcesContent":["import type { Reporter } from './reporter'\n\nexport const devReporter: Reporter = (diagnostic) => {\n if (import.meta.hot && typeof import.meta.hot.send === 'function') {\n import.meta.hot.send('nostics:report', diagnostic)\n }\n else {\n console.warn('[nostics]: import.meta.hot.send() is not available. This must be running on Vite.')\n }\n}\n"],"mappings":";AAEA,MAAa,eAAyB,eAAe;AACnD,KAAI,OAAO,KAAK,OAAO,OAAO,OAAO,KAAK,IAAI,SAAS,WACrD,QAAO,KAAK,IAAI,KAAK,kBAAkB,WAAW;KAGlD,SAAQ,KAAK,oFAAoF"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//#region src/utils.d.ts
|
|
2
|
+
type Simplify<T> = { [K in keyof T]: T[K] } & {};
|
|
3
|
+
type ExtractFieldParams<T> = T extends ((params: infer P) => string) ? P : Record<never, never>;
|
|
4
|
+
type ExtractParams<T> = (T extends {
|
|
5
|
+
message: infer M;
|
|
6
|
+
} ? ExtractFieldParams<M> : Record<never, never>) & (T extends {
|
|
7
|
+
fix: infer F;
|
|
8
|
+
} ? ExtractFieldParams<F> : Record<never, never>) & (T extends {
|
|
9
|
+
why: infer W;
|
|
10
|
+
} ? ExtractFieldParams<W> : Record<never, never>) & (T extends {
|
|
11
|
+
hint: infer H;
|
|
12
|
+
} ? ExtractFieldParams<H> : Record<never, never>);
|
|
13
|
+
type IsEmptyObject<T> = keyof T extends never ? true : false;
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/diagnostics.d.ts
|
|
16
|
+
type DiagnosticLevel = "error" | "warn" | "suggestion" | "deprecation";
|
|
17
|
+
interface SourceLocation {
|
|
18
|
+
file?: string;
|
|
19
|
+
line?: number;
|
|
20
|
+
column?: number;
|
|
21
|
+
}
|
|
22
|
+
interface Diagnostic {
|
|
23
|
+
code: string;
|
|
24
|
+
level: DiagnosticLevel;
|
|
25
|
+
message: string;
|
|
26
|
+
why?: string;
|
|
27
|
+
fix?: string;
|
|
28
|
+
hint?: string;
|
|
29
|
+
docs?: string;
|
|
30
|
+
sources?: SourceLocation[];
|
|
31
|
+
cause?: unknown;
|
|
32
|
+
context?: Record<string, unknown>;
|
|
33
|
+
stack?: string;
|
|
34
|
+
}
|
|
35
|
+
type Overrides = Partial<Pick<Diagnostic, "level" | "sources" | "cause" | "context">>;
|
|
36
|
+
type MessageTemplate<P = any> = string | ((params: P) => string);
|
|
37
|
+
interface DiagnosticDefinition {
|
|
38
|
+
message: MessageTemplate;
|
|
39
|
+
fix?: MessageTemplate;
|
|
40
|
+
why?: MessageTemplate;
|
|
41
|
+
hint?: MessageTemplate;
|
|
42
|
+
level?: DiagnosticLevel;
|
|
43
|
+
}
|
|
44
|
+
type CodeFactory<T> = IsEmptyObject<ExtractParams<T>> extends true ? (overrides?: Overrides) => Diagnostic : (params: Simplify<ExtractParams<T>>, overrides?: Overrides) => Diagnostic;
|
|
45
|
+
interface DiagnosticsMethods<C extends Record<string, DiagnosticDefinition>> {
|
|
46
|
+
codes: () => (keyof C & string)[];
|
|
47
|
+
has: (code: string) => code is Extract<keyof C, string>;
|
|
48
|
+
get: <K extends keyof C>(code: K) => C[K];
|
|
49
|
+
extend: <U extends Record<string, DiagnosticDefinition>>(defs: U) => DiagnosticsResult<C & U>;
|
|
50
|
+
}
|
|
51
|
+
type DiagnosticsResult<C extends Record<string, DiagnosticDefinition>> = { [K in keyof C]: CodeFactory<C[K]> } & DiagnosticsMethods<C>;
|
|
52
|
+
interface DefineDiagnosticsOptions<C extends Record<string, DiagnosticDefinition>> {
|
|
53
|
+
docsBase?: string | ((code: string) => string | undefined);
|
|
54
|
+
codes: C;
|
|
55
|
+
}
|
|
56
|
+
declare function defineDiagnostics<C extends Record<string, DiagnosticDefinition>>(options: DefineDiagnosticsOptions<C>): DiagnosticsResult<C>;
|
|
57
|
+
//#endregion
|
|
58
|
+
export { DiagnosticLevel as a, Overrides as c, DiagnosticDefinition as i, SourceLocation as l, DefineDiagnosticsOptions as n, DiagnosticsMethods as o, Diagnostic as r, DiagnosticsResult as s, CodeFactory as t, defineDiagnostics as u };
|
|
59
|
+
//# sourceMappingURL=diagnostics-CSUYJHfP.d.mts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { r as Diagnostic } from "./diagnostics-CSUYJHfP.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/format.d.ts
|
|
4
|
+
type Formatter = (diagnostic: Diagnostic) => string;
|
|
5
|
+
declare function formatTag(d: Diagnostic): string;
|
|
6
|
+
declare function renderFrame(d: Diagnostic): string;
|
|
7
|
+
declare const plainFormatter: Formatter;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { renderFrame as i, formatTag as n, plainFormatter as r, Formatter as t };
|
|
10
|
+
//# sourceMappingURL=format-CKWeokpQ.d.mts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/format.ts
|
|
2
|
+
function formatTag(d) {
|
|
3
|
+
return `[${d.code}]`;
|
|
4
|
+
}
|
|
5
|
+
function renderFrame(d) {
|
|
6
|
+
const header = `${formatTag(d)} ${d.message}`;
|
|
7
|
+
const details = [];
|
|
8
|
+
if (d.why) details.push(`why: ${d.why}`);
|
|
9
|
+
if (d.fix) details.push(`fix: ${d.fix}`);
|
|
10
|
+
if (d.hint) details.push(`hint: ${d.hint}`);
|
|
11
|
+
if (d.docs) details.push(`see: ${d.docs}`);
|
|
12
|
+
if (details.length === 0) return header;
|
|
13
|
+
return [header, ...details.map((detail, i) => {
|
|
14
|
+
return `${i < details.length - 1 ? "├▶" : "╰▶"} ${detail}`;
|
|
15
|
+
})].join("\n");
|
|
16
|
+
}
|
|
17
|
+
const plainFormatter = renderFrame;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { plainFormatter as n, renderFrame as r, formatTag as t };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=format-DYh8khIQ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-DYh8khIQ.mjs","names":[],"sources":["../src/format.ts"],"sourcesContent":["import type { Diagnostic } from './diagnostics'\n\nexport type Formatter = (diagnostic: Diagnostic) => string\n\nexport function formatTag(d: Diagnostic): string {\n return `[${d.code}]`\n}\n\nexport function renderFrame(d: Diagnostic): string {\n const header = `${formatTag(d)} ${d.message}`\n\n const details: string[] = []\n if (d.why)\n details.push(`why: ${d.why}`)\n if (d.fix)\n details.push(`fix: ${d.fix}`)\n if (d.hint)\n details.push(`hint: ${d.hint}`)\n if (d.docs)\n details.push(`see: ${d.docs}`)\n\n if (details.length === 0)\n return header\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\nexport const plainFormatter: Formatter = renderFrame\n"],"mappings":";AAIA,SAAgB,UAAU,GAAuB;AAC/C,QAAO,IAAI,EAAE,KAAK;;AAGpB,SAAgB,YAAY,GAAuB;CACjD,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC,GAAG,EAAE;CAEpC,MAAM,UAAoB,EAAE;AAC5B,KAAI,EAAE,IACJ,SAAQ,KAAK,QAAQ,EAAE,MAAM;AAC/B,KAAI,EAAE,IACJ,SAAQ,KAAK,QAAQ,EAAE,MAAM;AAC/B,KAAI,EAAE,KACJ,SAAQ,KAAK,SAAS,EAAE,OAAO;AACjC,KAAI,EAAE,KACJ,SAAQ,KAAK,QAAQ,EAAE,OAAO;AAEhC,KAAI,QAAQ,WAAW,EACrB,QAAO;AAOT,QAAO,CAAC,QAAQ,GALF,QAAQ,KAAK,QAAQ,MAAM;AAEvC,SAAO,GADW,IAAI,QAAQ,SAAS,IAAI,OAAO,KAC9B,GAAG;GACvB,CAEuB,CAAC,KAAK,KAAK;;AAGtC,MAAa,iBAA4B"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { t as Formatter } from "../format-CKWeokpQ.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/formatters/ansi.d.ts
|
|
4
|
+
interface Colors {
|
|
5
|
+
red: (s: string) => string;
|
|
6
|
+
yellow: (s: string) => string;
|
|
7
|
+
cyan: (s: string) => string;
|
|
8
|
+
gray: (s: string) => string;
|
|
9
|
+
bold: (s: string) => string;
|
|
10
|
+
dim: (s: string) => string;
|
|
11
|
+
}
|
|
12
|
+
declare function ansiFormatter(colors: Colors): Formatter;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { Colors, ansiFormatter };
|
|
15
|
+
//# sourceMappingURL=ansi.d.mts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { t as formatTag } from "../format-DYh8khIQ.mjs";
|
|
2
|
+
//#region src/formatters/ansi.ts
|
|
3
|
+
function levelColor(colors, level) {
|
|
4
|
+
switch (level) {
|
|
5
|
+
case "error": return colors.red;
|
|
6
|
+
case "warn":
|
|
7
|
+
case "deprecation": return colors.yellow;
|
|
8
|
+
case "suggestion": return colors.cyan;
|
|
9
|
+
default: return (s) => s;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function ansiFormatter(colors) {
|
|
13
|
+
return (d) => {
|
|
14
|
+
const colorize = levelColor(colors, d.level);
|
|
15
|
+
const header = `${colors.bold(colorize(formatTag(d)))} ${d.message}`;
|
|
16
|
+
const details = [];
|
|
17
|
+
if (d.why) details.push(`${colors.dim("why:")} ${d.why}`);
|
|
18
|
+
if (d.fix) details.push(`${colors.dim("fix:")} ${d.fix}`);
|
|
19
|
+
if (d.hint) details.push(`${colors.dim("hint:")} ${colors.gray(d.hint)}`);
|
|
20
|
+
if (d.docs) details.push(`${colors.dim("see:")} ${colors.cyan(d.docs)}`);
|
|
21
|
+
if (details.length === 0) return header;
|
|
22
|
+
return [header, ...details.map((detail, i) => {
|
|
23
|
+
return `${i < details.length - 1 ? colors.dim("├▶") : colors.dim("╰▶")} ${detail}`;
|
|
24
|
+
})].join("\n");
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { ansiFormatter };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=ansi.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ansi.mjs","names":[],"sources":["../../src/formatters/ansi.ts"],"sourcesContent":["import type { Diagnostic } from '../diagnostics'\nimport type { Formatter } from '../format'\nimport { formatTag } from '../format'\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\nfunction levelColor(colors: Colors, level: string): (s: string) => string {\n switch (level) {\n case 'error':\n return colors.red\n case 'warn':\n case 'deprecation':\n return colors.yellow\n case 'suggestion':\n return colors.cyan\n default:\n return s => s\n }\n}\n\nexport function ansiFormatter(colors: Colors): Formatter {\n return (d: Diagnostic): string => {\n const colorize = levelColor(colors, d.level)\n const tag = colors.bold(colorize(formatTag(d)))\n const header = `${tag} ${d.message}`\n\n const details: string[] = []\n if (d.why)\n details.push(`${colors.dim('why:')} ${d.why}`)\n if (d.fix)\n details.push(`${colors.dim('fix:')} ${d.fix}`)\n if (d.hint)\n details.push(`${colors.dim('hint:')} ${colors.gray(d.hint)}`)\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 = i < details.length - 1\n ? colors.dim('├▶')\n : colors.dim('╰▶')\n return `${connector} ${detail}`\n })\n\n return [header, ...lines].join('\\n')\n }\n}\n"],"mappings":";;AAaA,SAAS,WAAW,QAAgB,OAAsC;AACxE,SAAQ,OAAR;EACE,KAAK,QACH,QAAO,OAAO;EAChB,KAAK;EACL,KAAK,cACH,QAAO,OAAO;EAChB,KAAK,aACH,QAAO,OAAO;EAChB,QACE,SAAO,MAAK;;;AAIlB,SAAgB,cAAc,QAA2B;AACvD,SAAQ,MAA0B;EAChC,MAAM,WAAW,WAAW,QAAQ,EAAE,MAAM;EAE5C,MAAM,SAAS,GADH,OAAO,KAAK,SAAS,UAAU,EAAE,CAAC,CAAC,CACzB,GAAG,EAAE;EAE3B,MAAM,UAAoB,EAAE;AAC5B,MAAI,EAAE,IACJ,SAAQ,KAAK,GAAG,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,MAAM;AAChD,MAAI,EAAE,IACJ,SAAQ,KAAK,GAAG,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,MAAM;AAChD,MAAI,EAAE,KACJ,SAAQ,KAAK,GAAG,OAAO,IAAI,QAAQ,CAAC,GAAG,OAAO,KAAK,EAAE,KAAK,GAAG;AAC/D,MAAI,EAAE,KACJ,SAAQ,KAAK,GAAG,OAAO,IAAI,OAAO,CAAC,GAAG,OAAO,KAAK,EAAE,KAAK,GAAG;AAE9D,MAAI,QAAQ,WAAW,EACrB,QAAO;AAST,SAAO,CAAC,QAAQ,GAPF,QAAQ,KAAK,QAAQ,MAAM;AAIvC,UAAO,GAHW,IAAI,QAAQ,SAAS,IACnC,OAAO,IAAI,KAAK,GAChB,OAAO,IAAI,KAAK,CACA,GAAG;IACvB,CAEuB,CAAC,KAAK,KAAK"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.mjs","names":[],"sources":["../../src/formatters/json.ts"],"sourcesContent":["import type { Formatter } from '../format'\n\nexport const jsonFormatter: Formatter = diagnostic => JSON.stringify(diagnostic)\n"],"mappings":";AAEA,MAAa,iBAA2B,eAAc,KAAK,UAAU,WAAW"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { a as DiagnosticLevel, c as Overrides, i as DiagnosticDefinition, l as SourceLocation, n as DefineDiagnosticsOptions, o as DiagnosticsMethods, r as Diagnostic, s as DiagnosticsResult, t as CodeFactory, u as defineDiagnostics } from "./diagnostics-CSUYJHfP.mjs";
|
|
2
|
+
import { n as consoleReporter, r as createFetchReporter, t as Reporter } from "./reporter-DDQiiD6l.mjs";
|
|
3
|
+
import { i as renderFrame, n as formatTag, r as plainFormatter, t as Formatter } from "./format-CKWeokpQ.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/error.d.ts
|
|
6
|
+
declare class CodedError extends Error {
|
|
7
|
+
readonly diagnostic: Diagnostic;
|
|
8
|
+
constructor(diagnostic: Diagnostic);
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/logger.d.ts
|
|
12
|
+
interface DiagnosticActions extends Diagnostic {
|
|
13
|
+
throw: () => never;
|
|
14
|
+
warn: () => void;
|
|
15
|
+
error: () => void;
|
|
16
|
+
log: () => void;
|
|
17
|
+
format: () => string;
|
|
18
|
+
}
|
|
19
|
+
type ActionFactories<T> = { [K in keyof T as T[K] extends ((...args: any[]) => Diagnostic) ? K : never]: T[K] extends ((...args: infer A) => Diagnostic) ? (...args: A) => DiagnosticActions : never };
|
|
20
|
+
type MergeFactories<D extends readonly any[]> = D extends readonly [infer First, ...infer Rest] ? ActionFactories<First> & MergeFactories<Rest> : {};
|
|
21
|
+
interface LoggerMethods {
|
|
22
|
+
throw: (diagnostic: Diagnostic) => never;
|
|
23
|
+
warn: (diagnostic: Diagnostic) => void;
|
|
24
|
+
error: (diagnostic: Diagnostic) => void;
|
|
25
|
+
log: (diagnostic: Diagnostic) => void;
|
|
26
|
+
format: (diagnostic: Diagnostic) => string;
|
|
27
|
+
}
|
|
28
|
+
type Logger<D extends readonly any[]> = MergeFactories<D> & LoggerMethods;
|
|
29
|
+
interface CreateLoggerOptions<D extends readonly any[]> {
|
|
30
|
+
diagnostics: [...D];
|
|
31
|
+
formatter?: Formatter;
|
|
32
|
+
reporter?: Reporter | Reporter[];
|
|
33
|
+
captureStack?: boolean;
|
|
34
|
+
}
|
|
35
|
+
declare function createLogger<const D extends readonly any[]>(options: CreateLoggerOptions<D>): Logger<D>;
|
|
36
|
+
//#endregion
|
|
37
|
+
export { type CodeFactory, CodedError, type CreateLoggerOptions, type DefineDiagnosticsOptions, type Diagnostic, type DiagnosticActions, type DiagnosticDefinition, type DiagnosticLevel, type DiagnosticsMethods, type DiagnosticsResult, type Formatter, type Logger, type LoggerMethods, type MergeFactories, type Overrides, type Reporter, type SourceLocation, consoleReporter, createFetchReporter, createLogger, defineDiagnostics, formatTag, plainFormatter, renderFrame };
|
|
38
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { n as plainFormatter, r as renderFrame, t as formatTag } from "./format-DYh8khIQ.mjs";
|
|
2
|
+
//#region src/diagnostics.ts
|
|
3
|
+
function resolveTemplate(template, params) {
|
|
4
|
+
if (template == null) return void 0;
|
|
5
|
+
if (typeof template === "function") return template(params);
|
|
6
|
+
return template;
|
|
7
|
+
}
|
|
8
|
+
function defineDiagnostics(options) {
|
|
9
|
+
const { docsBase, codes } = options;
|
|
10
|
+
const result = {};
|
|
11
|
+
const codeKeys = Object.keys(codes);
|
|
12
|
+
for (const code of codeKeys) {
|
|
13
|
+
const def = codes[code];
|
|
14
|
+
result[code] = (paramsOrOverrides, maybeOverrides) => {
|
|
15
|
+
const hasParams = typeof def.message === "function" || typeof def.fix === "function" || typeof def.why === "function" || typeof def.hint === "function";
|
|
16
|
+
const params = hasParams ? paramsOrOverrides : void 0;
|
|
17
|
+
const overrides = hasParams ? maybeOverrides : paramsOrOverrides;
|
|
18
|
+
const docs = typeof docsBase === "function" ? docsBase(code) : docsBase != null ? `${docsBase}/${code.toLowerCase()}` : void 0;
|
|
19
|
+
return {
|
|
20
|
+
code,
|
|
21
|
+
level: def.level ?? "error",
|
|
22
|
+
message: resolveTemplate(def.message, params),
|
|
23
|
+
...docs != null && { docs },
|
|
24
|
+
...resolveTemplate(def.fix, params) != null && { fix: resolveTemplate(def.fix, params) },
|
|
25
|
+
...resolveTemplate(def.why, params) != null && { why: resolveTemplate(def.why, params) },
|
|
26
|
+
...resolveTemplate(def.hint, params) != null && { hint: resolveTemplate(def.hint, params) },
|
|
27
|
+
...overrides
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
Object.defineProperties(result, {
|
|
32
|
+
codes: {
|
|
33
|
+
value: () => codeKeys,
|
|
34
|
+
enumerable: false
|
|
35
|
+
},
|
|
36
|
+
has: {
|
|
37
|
+
value: (code) => code in codes,
|
|
38
|
+
enumerable: false
|
|
39
|
+
},
|
|
40
|
+
get: {
|
|
41
|
+
value: (code) => codes[code],
|
|
42
|
+
enumerable: false
|
|
43
|
+
},
|
|
44
|
+
extend: {
|
|
45
|
+
value: (defs) => defineDiagnostics({
|
|
46
|
+
...options,
|
|
47
|
+
codes: {
|
|
48
|
+
...codes,
|
|
49
|
+
...defs
|
|
50
|
+
}
|
|
51
|
+
}),
|
|
52
|
+
enumerable: false
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/error.ts
|
|
59
|
+
var CodedError = class extends Error {
|
|
60
|
+
diagnostic;
|
|
61
|
+
constructor(diagnostic) {
|
|
62
|
+
super(`[${diagnostic.code}] ${diagnostic.message}`);
|
|
63
|
+
this.name = "CodedError";
|
|
64
|
+
this.diagnostic = diagnostic;
|
|
65
|
+
if (diagnostic.cause != null) this.cause = diagnostic.cause;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/reporter.ts
|
|
70
|
+
const consoleReporter = (diagnostic, formatted) => {
|
|
71
|
+
if (diagnostic.level === "error") console.error(formatted);
|
|
72
|
+
else console.warn(formatted);
|
|
73
|
+
};
|
|
74
|
+
function createFetchReporter(url) {
|
|
75
|
+
return (diagnostic, _formatted) => {
|
|
76
|
+
fetch(url, {
|
|
77
|
+
method: "POST",
|
|
78
|
+
headers: { "Content-Type": "application/json" },
|
|
79
|
+
body: JSON.stringify(diagnostic)
|
|
80
|
+
}).catch(() => {});
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/logger.ts
|
|
85
|
+
function captureStackTrace() {
|
|
86
|
+
const err = {};
|
|
87
|
+
if (typeof Error.captureStackTrace === "function") Error.captureStackTrace(err, captureStackTrace);
|
|
88
|
+
else err.stack = (/* @__PURE__ */ new Error()).stack;
|
|
89
|
+
if (!err.stack) return void 0;
|
|
90
|
+
const lines = err.stack.split("\n");
|
|
91
|
+
let frameStart = 0;
|
|
92
|
+
while (frameStart < lines.length && !lines[frameStart].trimStart().startsWith("at ")) frameStart++;
|
|
93
|
+
const skipInternal = typeof Error.captureStackTrace === "function" ? 1 : 2;
|
|
94
|
+
const frames = lines.slice(frameStart + skipInternal).filter((line) => !line.includes("/node_modules/") && !line.includes("(node:")).map((line) => line.trim());
|
|
95
|
+
return frames.length > 0 ? frames.join("\n") : void 0;
|
|
96
|
+
}
|
|
97
|
+
function formatAndReport(formatter, reporters, diagnostic) {
|
|
98
|
+
const formatted = formatter(diagnostic);
|
|
99
|
+
for (const reporter of reporters) reporter(diagnostic, formatted);
|
|
100
|
+
return formatted;
|
|
101
|
+
}
|
|
102
|
+
function createActions(diagnostic, formatter, reporters, shouldCaptureStack) {
|
|
103
|
+
return Object.assign({}, diagnostic, {
|
|
104
|
+
throw() {
|
|
105
|
+
const stack = shouldCaptureStack ? captureStackTrace() : void 0;
|
|
106
|
+
const d = stack ? {
|
|
107
|
+
...diagnostic,
|
|
108
|
+
stack
|
|
109
|
+
} : diagnostic;
|
|
110
|
+
formatAndReport(formatter, reporters, d);
|
|
111
|
+
throw new CodedError(d);
|
|
112
|
+
},
|
|
113
|
+
warn() {
|
|
114
|
+
const stack = shouldCaptureStack ? captureStackTrace() : void 0;
|
|
115
|
+
formatAndReport(formatter, reporters, {
|
|
116
|
+
...diagnostic,
|
|
117
|
+
level: "warn",
|
|
118
|
+
...stack && { stack }
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
error() {
|
|
122
|
+
const stack = shouldCaptureStack ? captureStackTrace() : void 0;
|
|
123
|
+
formatAndReport(formatter, reporters, {
|
|
124
|
+
...diagnostic,
|
|
125
|
+
level: "error",
|
|
126
|
+
...stack && { stack }
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
log() {
|
|
130
|
+
const stack = shouldCaptureStack ? captureStackTrace() : void 0;
|
|
131
|
+
formatAndReport(formatter, reporters, stack ? {
|
|
132
|
+
...diagnostic,
|
|
133
|
+
stack
|
|
134
|
+
} : diagnostic);
|
|
135
|
+
},
|
|
136
|
+
format() {
|
|
137
|
+
return formatter(diagnostic);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function createLogger(options) {
|
|
142
|
+
const formatter = options.formatter ?? plainFormatter;
|
|
143
|
+
const reporters = Array.isArray(options.reporter) ? options.reporter : [options.reporter ?? consoleReporter];
|
|
144
|
+
const shouldCaptureStack = options.captureStack !== false;
|
|
145
|
+
const result = {};
|
|
146
|
+
for (const diagnostics of options.diagnostics) {
|
|
147
|
+
const codeKeys = typeof diagnostics.codes === "function" ? diagnostics.codes() : Object.keys(diagnostics);
|
|
148
|
+
for (const code of codeKeys) if (typeof diagnostics[code] === "function") result[code] = (...args) => {
|
|
149
|
+
return createActions(diagnostics[code](...args), formatter, reporters, shouldCaptureStack);
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
result.throw = (diagnostic) => {
|
|
153
|
+
const stack = shouldCaptureStack ? captureStackTrace() : void 0;
|
|
154
|
+
const d = stack ? {
|
|
155
|
+
...diagnostic,
|
|
156
|
+
stack
|
|
157
|
+
} : diagnostic;
|
|
158
|
+
formatAndReport(formatter, reporters, d);
|
|
159
|
+
throw new CodedError(d);
|
|
160
|
+
};
|
|
161
|
+
result.warn = (diagnostic) => {
|
|
162
|
+
const stack = shouldCaptureStack ? captureStackTrace() : void 0;
|
|
163
|
+
formatAndReport(formatter, reporters, {
|
|
164
|
+
...diagnostic,
|
|
165
|
+
level: "warn",
|
|
166
|
+
...stack && { stack }
|
|
167
|
+
});
|
|
168
|
+
};
|
|
169
|
+
result.error = (diagnostic) => {
|
|
170
|
+
const stack = shouldCaptureStack ? captureStackTrace() : void 0;
|
|
171
|
+
formatAndReport(formatter, reporters, {
|
|
172
|
+
...diagnostic,
|
|
173
|
+
level: "error",
|
|
174
|
+
...stack && { stack }
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
result.log = (diagnostic) => {
|
|
178
|
+
const stack = shouldCaptureStack ? captureStackTrace() : void 0;
|
|
179
|
+
formatAndReport(formatter, reporters, stack ? {
|
|
180
|
+
...diagnostic,
|
|
181
|
+
stack
|
|
182
|
+
} : diagnostic);
|
|
183
|
+
};
|
|
184
|
+
result.format = (diagnostic) => {
|
|
185
|
+
return formatter(diagnostic);
|
|
186
|
+
};
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
//#endregion
|
|
190
|
+
export { CodedError, consoleReporter, createFetchReporter, createLogger, defineDiagnostics, formatTag, plainFormatter, renderFrame };
|
|
191
|
+
|
|
192
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/diagnostics.ts","../src/error.ts","../src/reporter.ts","../src/logger.ts"],"sourcesContent":["import type { ExtractParams, IsEmptyObject, Simplify } from './utils'\n\nexport type DiagnosticLevel = 'error' | 'warn' | 'suggestion' | 'deprecation'\n\nexport interface SourceLocation {\n file?: string\n line?: number\n column?: number\n}\n\nexport interface Diagnostic {\n code: string\n level: DiagnosticLevel\n message: string\n why?: string\n fix?: string\n hint?: string\n docs?: string\n sources?: SourceLocation[]\n cause?: unknown\n context?: Record<string, unknown>\n stack?: string\n}\n\nexport type Overrides = Partial<Pick<Diagnostic, 'level' | 'sources' | 'cause' | 'context'>>\n\nexport type MessageTemplate<P = any> = string | ((params: P) => string)\n\nexport interface DiagnosticDefinition {\n message: MessageTemplate\n fix?: MessageTemplate\n why?: MessageTemplate\n hint?: MessageTemplate\n level?: DiagnosticLevel\n}\n\nexport type CodeFactory<T>\n = IsEmptyObject<ExtractParams<T>> extends true\n ? (overrides?: Overrides) => Diagnostic\n : (params: Simplify<ExtractParams<T>>, overrides?: Overrides) => Diagnostic\n\nexport interface DiagnosticsMethods<C extends Record<string, DiagnosticDefinition>> {\n codes: () => (keyof C & string)[]\n has: (code: string) => code is Extract<keyof C, string>\n get: <K extends keyof C>(code: K) => C[K]\n extend: <U extends Record<string, DiagnosticDefinition>>(defs: U) => DiagnosticsResult<C & U>\n}\n\nexport type DiagnosticsResult<C extends Record<string, DiagnosticDefinition>> = {\n [K in keyof C]: CodeFactory<C[K]>\n} & DiagnosticsMethods<C>\n\nexport interface DefineDiagnosticsOptions<C extends Record<string, DiagnosticDefinition>> {\n docsBase?: string | ((code: string) => string | undefined)\n codes: C\n}\n\nfunction resolveTemplate(template: MessageTemplate | undefined, params: any): string | undefined {\n if (template == null)\n return undefined\n if (typeof template === 'function')\n return template(params)\n return template\n}\n\nexport function defineDiagnostics<C extends Record<string, DiagnosticDefinition>>(\n options: DefineDiagnosticsOptions<C>,\n): DiagnosticsResult<C> {\n const { docsBase, codes } = options\n\n const result = {} as any\n const codeKeys = Object.keys(codes)\n\n for (const code of codeKeys) {\n const def = codes[code]\n result[code] = (paramsOrOverrides?: any, maybeOverrides?: Overrides): Diagnostic => {\n // Determine if first arg is params or overrides\n const hasParams = typeof def.message === 'function'\n || typeof def.fix === 'function'\n || typeof def.why === 'function'\n || typeof def.hint === 'function'\n\n const params = hasParams ? paramsOrOverrides : undefined\n const overrides = hasParams ? maybeOverrides : paramsOrOverrides as Overrides | undefined\n\n const docs = typeof docsBase === 'function'\n ? docsBase(code)\n : docsBase != null\n ? `${docsBase}/${code.toLowerCase()}`\n : undefined\n\n const diagnostic: Diagnostic = {\n code,\n level: def.level ?? 'error',\n message: resolveTemplate(def.message, params)!,\n ...(docs != null && { docs }),\n ...resolveTemplate(def.fix, params) != null && { fix: resolveTemplate(def.fix, params) },\n ...resolveTemplate(def.why, params) != null && { why: resolveTemplate(def.why, params) },\n ...resolveTemplate(def.hint, params) != null && { hint: resolveTemplate(def.hint, params) },\n ...overrides,\n }\n\n return diagnostic\n }\n }\n\n Object.defineProperties(result, {\n codes: {\n value: () => codeKeys,\n enumerable: false,\n },\n has: {\n value: (code: string) => code in codes,\n enumerable: false,\n },\n get: {\n value: (code: string) => codes[code],\n enumerable: false,\n },\n extend: {\n value: <U extends Record<string, DiagnosticDefinition>>(defs: U) =>\n defineDiagnostics({ ...options, codes: { ...codes, ...defs } as any }),\n enumerable: false,\n },\n })\n\n return result as DiagnosticsResult<C>\n}\n","import type { Diagnostic } from './diagnostics'\n\nexport class CodedError extends Error {\n readonly diagnostic: Diagnostic\n\n constructor(diagnostic: Diagnostic) {\n super(`[${diagnostic.code}] ${diagnostic.message}`)\n this.name = 'CodedError'\n this.diagnostic = diagnostic\n if (diagnostic.cause != null)\n this.cause = diagnostic.cause\n }\n}\n","import type { Diagnostic } from './diagnostics'\n\nexport type Reporter = (diagnostic: Diagnostic, formatted: string) => void\n\nexport const consoleReporter: Reporter = (diagnostic, formatted) => {\n if (diagnostic.level === 'error')\n console.error(formatted)\n else\n console.warn(formatted)\n}\n\nexport function createFetchReporter(url: string): Reporter {\n return (diagnostic, _formatted) => {\n fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(diagnostic),\n }).catch(() => { })\n }\n}\n","import type { Diagnostic } from './diagnostics'\nimport type { Formatter } from './format'\nimport type { Reporter } from './reporter'\nimport { CodedError } from './error'\nimport { plainFormatter } from './format'\nimport { consoleReporter } from './reporter'\n\nexport interface DiagnosticActions extends Diagnostic {\n throw: () => never\n warn: () => void\n error: () => void\n log: () => void\n format: () => string\n}\n\n// Type utilities for extracting params from template fields\n\ntype ActionFactories<T> = {\n [K in keyof T as T[K] extends (...args: any[]) => Diagnostic ? K : never]:\n T[K] extends (...args: infer A) => Diagnostic\n ? (...args: A) => DiagnosticActions\n : never\n}\n\nexport type MergeFactories<D extends readonly any[]>\n = D extends readonly [infer First, ...infer Rest]\n ? ActionFactories<First> & MergeFactories<Rest>\n // eslint-disable-next-line ts/no-empty-object-type\n : {}\n\nexport interface LoggerMethods {\n throw: (diagnostic: Diagnostic) => never\n warn: (diagnostic: Diagnostic) => void\n error: (diagnostic: Diagnostic) => void\n log: (diagnostic: Diagnostic) => void\n format: (diagnostic: Diagnostic) => string\n}\n\nexport type Logger<D extends readonly any[]> = MergeFactories<D> & LoggerMethods\n\nexport interface CreateLoggerOptions<D extends readonly any[]> {\n diagnostics: [...D]\n formatter?: Formatter\n reporter?: Reporter | Reporter[]\n captureStack?: boolean\n}\n\nfunction captureStackTrace(): string | undefined {\n const err: { stack?: string } = {}\n if (typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(err, captureStackTrace)\n }\n else {\n // eslint-disable-next-line unicorn/error-message -- only used for stack capture\n err.stack = new Error().stack\n }\n if (!err.stack)\n return undefined\n\n const lines = err.stack.split('\\n')\n\n // Find where \"at \" frames begin (skip \"Error\" header)\n let frameStart = 0\n while (frameStart < lines.length && !lines[frameStart].trimStart().startsWith('at ')) {\n frameStart++\n }\n\n // V8 captureStackTrace already strips captureStackTrace itself,\n // fallback needs to skip captureStackTrace + the action method (2 frames)\n const skipInternal = typeof Error.captureStackTrace === 'function' ? 1 : 2\n\n const frames = lines\n .slice(frameStart + skipInternal)\n .filter(line => !line.includes('/node_modules/') && !line.includes('(node:'))\n .map(line => line.trim())\n\n return frames.length > 0 ? frames.join('\\n') : undefined\n}\n\nfunction formatAndReport(formatter: Formatter, reporters: Reporter[], diagnostic: Diagnostic): string {\n const formatted = formatter(diagnostic)\n for (const reporter of reporters)\n reporter(diagnostic, formatted)\n return formatted\n}\n\nfunction createActions(\n diagnostic: Diagnostic,\n formatter: Formatter,\n reporters: Reporter[],\n shouldCaptureStack: boolean,\n): DiagnosticActions {\n return Object.assign({}, diagnostic, {\n throw(): never {\n const stack = shouldCaptureStack ? captureStackTrace() : undefined\n const d = stack ? { ...diagnostic, stack } : diagnostic\n formatAndReport(formatter, reporters, d)\n throw new CodedError(d)\n },\n warn() {\n const stack = shouldCaptureStack ? captureStackTrace() : undefined\n formatAndReport(formatter, reporters, { ...diagnostic, level: 'warn' as const, ...(stack && { stack }) })\n },\n error() {\n const stack = shouldCaptureStack ? captureStackTrace() : undefined\n formatAndReport(formatter, reporters, { ...diagnostic, level: 'error' as const, ...(stack && { stack }) })\n },\n log() {\n const stack = shouldCaptureStack ? captureStackTrace() : undefined\n const d = stack ? { ...diagnostic, stack } : diagnostic\n formatAndReport(formatter, reporters, d)\n },\n format() {\n return formatter(diagnostic)\n },\n })\n}\n\nexport function createLogger<const D extends readonly any[]>(\n options: CreateLoggerOptions<D>,\n): Logger<D> {\n const formatter = options.formatter ?? plainFormatter\n const reporters = Array.isArray(options.reporter)\n ? options.reporter\n : [options.reporter ?? consoleReporter]\n const shouldCaptureStack = options.captureStack !== false\n\n const result = {} as any\n\n // Merge all diagnostic code factories\n for (const diagnostics of options.diagnostics) {\n const codeKeys = typeof diagnostics.codes === 'function'\n ? diagnostics.codes()\n : Object.keys(diagnostics)\n\n for (const code of codeKeys) {\n if (typeof diagnostics[code] === 'function') {\n result[code] = (...args: any[]) => {\n const diagnostic = diagnostics[code](...args)\n return createActions(diagnostic, formatter, reporters, shouldCaptureStack)\n }\n }\n }\n }\n\n // Raw logger methods\n result.throw = (diagnostic: Diagnostic): never => {\n const stack = shouldCaptureStack ? captureStackTrace() : undefined\n const d = stack ? { ...diagnostic, stack } : diagnostic\n formatAndReport(formatter, reporters, d)\n throw new CodedError(d)\n }\n result.warn = (diagnostic: Diagnostic): void => {\n const stack = shouldCaptureStack ? captureStackTrace() : undefined\n formatAndReport(formatter, reporters, { ...diagnostic, level: 'warn' as const, ...(stack && { stack }) })\n }\n result.error = (diagnostic: Diagnostic): void => {\n const stack = shouldCaptureStack ? captureStackTrace() : undefined\n formatAndReport(formatter, reporters, { ...diagnostic, level: 'error' as const, ...(stack && { stack }) })\n }\n result.log = (diagnostic: Diagnostic): void => {\n const stack = shouldCaptureStack ? captureStackTrace() : undefined\n const d = stack ? { ...diagnostic, stack } : diagnostic\n formatAndReport(formatter, reporters, d)\n }\n result.format = (diagnostic: Diagnostic): string => {\n return formatter(diagnostic)\n }\n\n return result as Logger<D>\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { r as Diagnostic } from "./diagnostics-CSUYJHfP.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/reporter.d.ts
|
|
4
|
+
type Reporter = (diagnostic: Diagnostic, formatted: string) => void;
|
|
5
|
+
declare const consoleReporter: Reporter;
|
|
6
|
+
declare function createFetchReporter(url: string): Reporter;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { consoleReporter as n, createFetchReporter as r, Reporter as t };
|
|
9
|
+
//# sourceMappingURL=reporter-DDQiiD6l.d.mts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { UnpluginInstance } from "unplugin";
|
|
2
|
+
import MagicString from "magic-string";
|
|
3
|
+
|
|
4
|
+
//#region src/code-transform/transform.d.ts
|
|
5
|
+
interface TransformOptions {
|
|
6
|
+
/**
|
|
7
|
+
* The package name to detect imports from.
|
|
8
|
+
* @default 'nostics'
|
|
9
|
+
*/
|
|
10
|
+
packageName?: string;
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/code-transform/server-plugin.d.ts
|
|
14
|
+
interface LogsSdkServerOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Path to the log file.
|
|
17
|
+
* @default '.nostics.log'
|
|
18
|
+
*/
|
|
19
|
+
logFile?: string;
|
|
20
|
+
}
|
|
21
|
+
declare const logsSDKServer: UnpluginInstance<LogsSdkServerOptions | undefined>;
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/code-transform/unplugin.d.ts
|
|
24
|
+
type LogsSdkPluginOptions = TransformOptions;
|
|
25
|
+
declare const logsSDK: UnpluginInstance<LogsSdkPluginOptions | undefined>;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { LogsSdkPluginOptions, type LogsSdkServerOptions, logsSDK as default, logsSDK, logsSDKServer };
|
|
28
|
+
//# sourceMappingURL=unplugin.d.mts.map
|