ag-ui-validate 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -26
- package/dist/{catalog-DqTI4VLt.js → catalog-3e6INLCp.js} +120 -2
- package/dist/catalog-3e6INLCp.js.map +1 -0
- package/dist/{catalog-AB5vuZ8j.cjs → catalog-DH5gDIbo.cjs} +120 -2
- package/dist/catalog-DH5gDIbo.cjs.map +1 -0
- package/dist/cli.js +639 -61
- package/dist/cli.js.map +1 -1
- package/dist/{index-Hmqj3r_r.d.cts → index-Cydfs-rB.d.ts} +4 -4
- package/dist/{index-oNG1kOp9.d.ts → index-DsVmjcLO.d.cts} +4 -4
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/report.cjs +11 -2
- package/dist/report.cjs.map +1 -1
- package/dist/report.d.cts +21 -12
- package/dist/report.d.ts +21 -12
- package/dist/report.js +11 -2
- package/dist/report.js.map +1 -1
- package/dist/{src-Dcuqz2QC.js → src-CezZ73Mm.js} +494 -59
- package/dist/src-CezZ73Mm.js.map +1 -0
- package/dist/{src-D04i3Vso.cjs → src-CyugLoks.cjs} +494 -59
- package/dist/src-CyugLoks.cjs.map +1 -0
- package/dist/transport.cjs +1 -1
- package/dist/transport.d.cts +1 -1
- package/dist/transport.d.ts +1 -1
- package/dist/transport.js +1 -1
- package/dist/{types-oH_QTnn2.d.ts → types-CUIgyj2N.d.cts} +3 -1
- package/dist/{types-oH_QTnn2.d.cts → types-CUIgyj2N.d.ts} +3 -1
- package/dist/vitest.js +614 -59
- package/dist/vitest.js.map +1 -1
- package/js/src/cli-args.ts +16 -0
- package/js/src/cli.ts +1 -1
- package/js/src/index.ts +5 -0
- package/js/src/protocol/event-table.ts +93 -2
- package/js/src/report/json.ts +14 -3
- package/js/src/report/sarif.ts +3 -6
- package/js/src/rules/catalog.ts +4 -0
- package/js/src/rules/checks/context.ts +48 -3
- package/js/src/rules/checks/lifecycle.ts +5 -2
- package/js/src/rules/checks/subagents.ts +78 -0
- package/js/src/rules/checks/text.ts +11 -3
- package/js/src/rules/checks/toolcalls.ts +8 -2
- package/package.json +2 -2
- package/spec/catalog.json +108 -1
- package/spec/event-categories.json +6 -2
- package/spec/fixtures/invalid/AGUI501-missing-data-prefix/scenario.json +3 -3
- package/spec/fixtures/invalid/AGUI502-payload-not-json/stream.jsonl +2 -2
- package/spec/fixtures/invalid/AGUI503-unknown-event-type/expected.json +1 -1
- package/spec/fixtures/invalid/AGUI503-unknown-event-type/stream.jsonl +3 -3
- package/spec/fixtures/invalid/AGUI504-schema-violation/stream.jsonl +4 -4
- package/spec/fixtures/invalid/AGUI505-unexpected-content-type/scenario.json +5 -5
- package/spec/fixtures/invalid/AGUI506-keepalive-gap/scenario.json +5 -5
- package/spec/fixtures/invalid/AGUI507-buffered-response/scenario.json +1 -1
- package/spec/fixtures/invalid/AGUI508-connection-dropped/scenario.json +1 -1
- package/spec/fixtures/invalid/AGUI601-duplicate-subagent-started/expected.json +12 -0
- package/spec/fixtures/invalid/AGUI601-duplicate-subagent-started/stream.jsonl +5 -0
- package/spec/fixtures/invalid/AGUI602-finished-without-start/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI602-finished-without-start/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI603-error-without-start/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI603-error-without-start/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI604-subagent-unterminated/expected.json +10 -0
- package/spec/fixtures/invalid/AGUI604-subagent-unterminated/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI605-unknown-parent-subagent-run-id/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI605-unknown-parent-subagent-run-id/stream.jsonl +4 -0
- package/spec/fixtures/invalid/AGUI606-continuation-owner-mismatch/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI606-continuation-owner-mismatch/stream.jsonl +6 -0
- package/spec/fixtures/invalid/AGUI901-raw-wraps-typed-event/stream.jsonl +3 -3
- package/spec/fixtures/invalid/AGUI903-custom-name-not-namespaced/stream.jsonl +3 -3
- package/dist/catalog-AB5vuZ8j.cjs.map +0 -1
- package/dist/catalog-DqTI4VLt.js.map +0 -1
- package/dist/src-D04i3Vso.cjs.map +0 -1
- package/dist/src-Dcuqz2QC.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { d as ValidatorOptions, u as Validator } from "./types-
|
|
1
|
+
import { d as ValidatorOptions, u as Validator } from "./types-CUIgyj2N.js";
|
|
2
2
|
//#region src/protocol/event-table.d.ts
|
|
3
3
|
type FieldKind = "string" | "number" | "boolean" | "object" | "array" | "any";
|
|
4
|
-
type EventCategory = "lifecycle" | "text" | "toolcall" | "state" | "activity" | "reasoning" | "thinking" | "special";
|
|
4
|
+
type EventCategory = "lifecycle" | "text" | "toolcall" | "state" | "activity" | "reasoning" | "thinking" | "special" | "subagent";
|
|
5
5
|
interface FieldSpec {
|
|
6
6
|
kind: FieldKind;
|
|
7
7
|
required: boolean;
|
|
@@ -17,7 +17,7 @@ interface EventSpec {
|
|
|
17
17
|
fields: Readonly<Record<string, FieldSpec>>;
|
|
18
18
|
}
|
|
19
19
|
/** Version of @ag-ui/core this table was derived from. */
|
|
20
|
-
declare const SDK_VERSION = "0.0.
|
|
20
|
+
declare const SDK_VERSION = "0.0.59";
|
|
21
21
|
/** Canonical wire event types and their schemas, derived from @ag-ui/core. */
|
|
22
22
|
declare const EVENT_TABLE: Readonly<Record<string, EventSpec>>;
|
|
23
23
|
/** All canonical wire `type` values, in @ag-ui/core enum order. */
|
|
@@ -49,4 +49,4 @@ declare function applyPatch(doc: unknown, patch: unknown): PatchResult;
|
|
|
49
49
|
declare function createValidator(opts?: ValidatorOptions): Validator;
|
|
50
50
|
//#endregion
|
|
51
51
|
export { EVENT_TYPES as a, FieldKind as c, EVENT_TABLE as i, FieldSpec as l, applyPatch as n, EventCategory as o, validatePatchShape as r, EventSpec as s, createValidator as t, SDK_VERSION as u };
|
|
52
|
-
//# sourceMappingURL=index-
|
|
52
|
+
//# sourceMappingURL=index-Cydfs-rB.d.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { d as ValidatorOptions, u as Validator } from "./types-
|
|
1
|
+
import { d as ValidatorOptions, u as Validator } from "./types-CUIgyj2N.cjs";
|
|
2
2
|
//#region src/protocol/event-table.d.ts
|
|
3
3
|
type FieldKind = "string" | "number" | "boolean" | "object" | "array" | "any";
|
|
4
|
-
type EventCategory = "lifecycle" | "text" | "toolcall" | "state" | "activity" | "reasoning" | "thinking" | "special";
|
|
4
|
+
type EventCategory = "lifecycle" | "text" | "toolcall" | "state" | "activity" | "reasoning" | "thinking" | "special" | "subagent";
|
|
5
5
|
interface FieldSpec {
|
|
6
6
|
kind: FieldKind;
|
|
7
7
|
required: boolean;
|
|
@@ -17,7 +17,7 @@ interface EventSpec {
|
|
|
17
17
|
fields: Readonly<Record<string, FieldSpec>>;
|
|
18
18
|
}
|
|
19
19
|
/** Version of @ag-ui/core this table was derived from. */
|
|
20
|
-
declare const SDK_VERSION = "0.0.
|
|
20
|
+
declare const SDK_VERSION = "0.0.59";
|
|
21
21
|
/** Canonical wire event types and their schemas, derived from @ag-ui/core. */
|
|
22
22
|
declare const EVENT_TABLE: Readonly<Record<string, EventSpec>>;
|
|
23
23
|
/** All canonical wire `type` values, in @ag-ui/core enum order. */
|
|
@@ -49,4 +49,4 @@ declare function applyPatch(doc: unknown, patch: unknown): PatchResult;
|
|
|
49
49
|
declare function createValidator(opts?: ValidatorOptions): Validator;
|
|
50
50
|
//#endregion
|
|
51
51
|
export { EVENT_TYPES as a, FieldKind as c, EVENT_TABLE as i, FieldSpec as l, applyPatch as n, EventCategory as o, validatePatchShape as r, EventSpec as s, createValidator as t, SDK_VERSION as u };
|
|
52
|
-
//# sourceMappingURL=index-
|
|
52
|
+
//# sourceMappingURL=index-DsVmjcLO.d.cts.map
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_src = require("./src-
|
|
3
|
-
const require_catalog = require("./catalog-
|
|
2
|
+
const require_src = require("./src-CyugLoks.cjs");
|
|
3
|
+
const require_catalog = require("./catalog-DH5gDIbo.cjs");
|
|
4
4
|
exports.CANONICAL_FEATURES = require_src.CANONICAL_FEATURES;
|
|
5
5
|
exports.CATALOG = require_catalog.CATALOG;
|
|
6
6
|
exports.EVENT_TABLE = require_src.EVENT_TABLE;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { _ as SeverityOrOff, a as FeatureStatus, c as Summary, d as ValidatorOptions, f as CATALOG, g as Severity, h as RuleDefinition, i as FeatureMatrix, l as ValidationLayer, m as RULES, n as CanonicalFeature, o as Report, p as Catalog, r as Diagnostic, s as SkippedRule, t as CANONICAL_FEATURES, u as Validator, v as formatMessage, y as validateCatalog } from "./types-
|
|
2
|
-
import { a as EVENT_TYPES, c as FieldKind, i as EVENT_TABLE, l as FieldSpec, n as applyPatch, o as EventCategory, r as validatePatchShape, s as EventSpec, t as createValidator, u as SDK_VERSION } from "./index-
|
|
1
|
+
import { _ as SeverityOrOff, a as FeatureStatus, c as Summary, d as ValidatorOptions, f as CATALOG, g as Severity, h as RuleDefinition, i as FeatureMatrix, l as ValidationLayer, m as RULES, n as CanonicalFeature, o as Report, p as Catalog, r as Diagnostic, s as SkippedRule, t as CANONICAL_FEATURES, u as Validator, v as formatMessage, y as validateCatalog } from "./types-CUIgyj2N.cjs";
|
|
2
|
+
import { a as EVENT_TYPES, c as FieldKind, i as EVENT_TABLE, l as FieldSpec, n as applyPatch, o as EventCategory, r as validatePatchShape, s as EventSpec, t as createValidator, u as SDK_VERSION } from "./index-DsVmjcLO.cjs";
|
|
3
3
|
export { CANONICAL_FEATURES, CATALOG, CanonicalFeature, type Catalog, Diagnostic, EVENT_TABLE, EVENT_TYPES, type EventCategory, type EventSpec, FeatureMatrix, FeatureStatus, type FieldKind, type FieldSpec, RULES, Report, type RuleDefinition, type Severity as RuleSeverity, type Severity, SDK_VERSION, type SeverityOrOff, SkippedRule, Summary, ValidationLayer, Validator, ValidatorOptions, applyPatch, createValidator, formatMessage, validateCatalog, validatePatchShape };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { _ as SeverityOrOff, a as FeatureStatus, c as Summary, d as ValidatorOptions, f as CATALOG, g as Severity, h as RuleDefinition, i as FeatureMatrix, l as ValidationLayer, m as RULES, n as CanonicalFeature, o as Report, p as Catalog, r as Diagnostic, s as SkippedRule, t as CANONICAL_FEATURES, u as Validator, v as formatMessage, y as validateCatalog } from "./types-
|
|
2
|
-
import { a as EVENT_TYPES, c as FieldKind, i as EVENT_TABLE, l as FieldSpec, n as applyPatch, o as EventCategory, r as validatePatchShape, s as EventSpec, t as createValidator, u as SDK_VERSION } from "./index-
|
|
1
|
+
import { _ as SeverityOrOff, a as FeatureStatus, c as Summary, d as ValidatorOptions, f as CATALOG, g as Severity, h as RuleDefinition, i as FeatureMatrix, l as ValidationLayer, m as RULES, n as CanonicalFeature, o as Report, p as Catalog, r as Diagnostic, s as SkippedRule, t as CANONICAL_FEATURES, u as Validator, v as formatMessage, y as validateCatalog } from "./types-CUIgyj2N.js";
|
|
2
|
+
import { a as EVENT_TYPES, c as FieldKind, i as EVENT_TABLE, l as FieldSpec, n as applyPatch, o as EventCategory, r as validatePatchShape, s as EventSpec, t as createValidator, u as SDK_VERSION } from "./index-Cydfs-rB.js";
|
|
3
3
|
export { CANONICAL_FEATURES, CATALOG, CanonicalFeature, type Catalog, Diagnostic, EVENT_TABLE, EVENT_TYPES, type EventCategory, type EventSpec, FeatureMatrix, FeatureStatus, type FieldKind, type FieldSpec, RULES, Report, type RuleDefinition, type Severity as RuleSeverity, type Severity, SDK_VERSION, type SeverityOrOff, SkippedRule, Summary, ValidationLayer, Validator, ValidatorOptions, applyPatch, createValidator, formatMessage, validateCatalog, validatePatchShape };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as EVENT_TABLE, i as validatePatchShape, n as CANONICAL_FEATURES, o as EVENT_TYPES, r as applyPatch, s as SDK_VERSION, t as createValidator } from "./src-
|
|
2
|
-
import { i as validateCatalog, n as RULES, r as formatMessage, t as CATALOG } from "./catalog-
|
|
1
|
+
import { a as EVENT_TABLE, i as validatePatchShape, n as CANONICAL_FEATURES, o as EVENT_TYPES, r as applyPatch, s as SDK_VERSION, t as createValidator } from "./src-CezZ73Mm.js";
|
|
2
|
+
import { i as validateCatalog, n as RULES, r as formatMessage, t as CATALOG } from "./catalog-3e6INLCp.js";
|
|
3
3
|
export { CANONICAL_FEATURES, CATALOG, EVENT_TABLE, EVENT_TYPES, RULES, SDK_VERSION, applyPatch, createValidator, formatMessage, validateCatalog, validatePatchShape };
|
package/dist/report.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_catalog = require("./catalog-
|
|
2
|
+
const require_catalog = require("./catalog-DH5gDIbo.cjs");
|
|
3
3
|
//#region src/report/pretty.ts
|
|
4
4
|
const SYMBOL = {
|
|
5
5
|
error: "✖",
|
|
@@ -84,9 +84,17 @@ function formatReportSummary(report, opts) {
|
|
|
84
84
|
//#endregion
|
|
85
85
|
//#region src/report/json.ts
|
|
86
86
|
function toJsonReport(report, opts) {
|
|
87
|
+
const diagnostics = report.diagnostics.map((d) => {
|
|
88
|
+
const category = require_catalog.RULES.get(d.rule)?.category;
|
|
89
|
+
return category === void 0 ? { ...d } : {
|
|
90
|
+
...d,
|
|
91
|
+
category
|
|
92
|
+
};
|
|
93
|
+
});
|
|
87
94
|
const doc = {
|
|
88
95
|
tool: opts.tool,
|
|
89
|
-
...report
|
|
96
|
+
...report,
|
|
97
|
+
diagnostics
|
|
90
98
|
};
|
|
91
99
|
if (opts.target !== void 0) doc.target = opts.target;
|
|
92
100
|
return doc;
|
|
@@ -110,6 +118,7 @@ function toSarif(report, opts) {
|
|
|
110
118
|
if (catalog !== void 0) {
|
|
111
119
|
entry.shortDescription = { text: catalog.title };
|
|
112
120
|
entry.defaultConfiguration = { level: LEVEL[catalog.severity] };
|
|
121
|
+
entry.properties = { tags: [catalog.category] };
|
|
113
122
|
}
|
|
114
123
|
rules.set(d.rule, entry);
|
|
115
124
|
}
|
package/dist/report.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.cjs","names":["RULES","RULES"],"sources":["../js/src/report/pretty.ts","../js/src/report/json.ts","../js/src/report/sarif.ts","../js/src/report/junit.ts"],"sourcesContent":["// Human-readable output. Pure string formatting — the CLI decides where it\n// goes and whether a TTY wants color.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report, Severity } from \"../types.js\"\n\nexport interface PrettyOptions {\n color: boolean\n}\n\nconst SYMBOL: Record<Severity, string> = { error: \"✖\", warning: \"⚠\", info: \"ℹ\" }\nconst SGR: Record<Severity, string> = { error: \"31\", warning: \"33\", info: \"36\" }\n\nfunction paint(code: string, s: string, on: boolean): string {\n return on ? `\\x1b[${code}m${s}\\x1b[0m` : s\n}\n\nexport function formatDiagnosticLine(d: Diagnostic, opts: PrettyOptions): string {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"—\"\n const head = paint(SGR[d.severity], `${SYMBOL[d.severity]} ${d.rule}`, opts.color)\n const meta = paint(\"2\", `${d.severity.padEnd(7)} ${where.padEnd(10)}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${d.specUrl}`, opts.color)\n return `${head} ${meta} ${d.message}\\n${cite}`\n}\n\nfunction count(n: number, noun: string): string {\n return `${n} ${noun}${n === 1 ? \"\" : \"s\"}`\n}\n\nconst SEVERITY_RANK: Record<Severity, number> = { error: 0, warning: 1, info: 2 }\nconst SAMPLE_INDEXES = 3\n\n/**\n * One line per rule instead of one per occurrence — for large streams where\n * the same violation repeats. Totals stay honest in the summary; this only\n * changes what is listed.\n */\nexport function formatGroupedDiagnostics(diagnostics: Diagnostic[], opts: PrettyOptions): string {\n const groups = new Map<string, Diagnostic[]>()\n for (const d of diagnostics) {\n const list = groups.get(d.rule)\n if (list === undefined) groups.set(d.rule, [d])\n else list.push(d)\n }\n\n const sorted = [...groups.values()].sort((a, b) => {\n const bySeverity = SEVERITY_RANK[a[0]!.severity] - SEVERITY_RANK[b[0]!.severity]\n return bySeverity !== 0 ? bySeverity : a[0]!.rule.localeCompare(b[0]!.rule)\n })\n\n const lines: string[] = []\n for (const group of sorted) {\n const first = group[0]!\n const title = RULES.get(first.rule)?.title ?? first.message\n const indexes = group.map((d) => d.eventIndex).filter((i) => i >= 0)\n let where: string\n if (indexes.length === 0) {\n where = \"stream-level\"\n } else {\n const sample = indexes.slice(0, SAMPLE_INDEXES).join(\", \")\n const rest = indexes.length - SAMPLE_INDEXES\n where = `events ${sample}${rest > 0 ? ` (+${rest} more)` : \"\"}`\n }\n const head = paint(SGR[first.severity], `${SYMBOL[first.severity]} ${first.rule}`, opts.color)\n const meta = paint(\"2\", `${first.severity.padEnd(7)} ×${group.length}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${first.specUrl}`, opts.color)\n lines.push(`${head} ${meta} ${title} — ${where}`, cite)\n }\n return lines.join(\"\\n\")\n}\n\nexport function formatReportSummary(report: Report, opts: PrettyOptions): string {\n const { errors, warnings, info } = report.summary\n const lines: string[] = []\n\n if (errors + warnings + info === 0) {\n lines.push(\n paint(\"32\", `✔ no conformance violations across ${count(report.eventCount, \"event\")}`, opts.color),\n )\n } else {\n lines.push(\n `${count(errors, \"error\")}, ${count(warnings, \"warning\")}, ${info} info across ${count(report.eventCount, \"event\")}`,\n )\n }\n\n const features = Object.entries(report.features)\n const exercised = features.filter(([, s]) => s === \"exercised\").map(([f]) => f)\n const suffix = exercised.length > 0 ? `: ${exercised.join(\", \")}` : \"\"\n lines.push(`${exercised.length} of ${features.length} AG-UI features exercised${suffix}`)\n\n if (report.skipped.length > 0) {\n lines.push(`${count(report.skipped.length, \"rule\")} not evaluated:`)\n for (const s of report.skipped) {\n lines.push(paint(\"2\", ` – ${s.rule}: ${s.reason}`, opts.color))\n }\n }\n\n for (const e of report.internalErrors) {\n lines.push(paint(\"31\", `! internal validator error: ${e}`, opts.color))\n }\n\n return lines.join(\"\\n\")\n}\n","// Machine-readable report: the core Report plus tool identification, so a\n// stored document is self-describing.\nimport type { Report } from \"../types.js\"\n\nexport interface JsonReportOptions {\n tool: { name: string; version: string }\n /** What was validated: URL, file path, or \"stdin\". */\n target?: string\n}\n\nexport interface JsonReportDocument extends Report {\n tool: { name: string; version: string }\n target?: string\n}\n\nexport function toJsonReport(report: Report, opts: JsonReportOptions): JsonReportDocument {\n const doc: JsonReportDocument = { tool: opts.tool, ...report }\n if (opts.target !== undefined) doc.target = opts.target\n return doc\n}\n","// SARIF 2.1.0 output for code-scanning integrations (e.g. GitHub).\n// Level mapping: error→error, warning→warning, info→note. When the input was a\n// line-oriented file (JSONL/captured SSE fed line-per-event is not guaranteed,\n// so only the caller knows), event N is reported at line N+1 via artifactUri.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report } from \"../types.js\"\n\nexport interface SarifOptions {\n toolVersion: string\n /** URI of the validated artifact; enables line-based locations. */\n artifactUri?: string\n}\n\nexport type SarifLevel = \"error\" | \"warning\" | \"note\"\n\nexport interface SarifResult {\n ruleId: string\n level: SarifLevel\n message: { text: string }\n locations: Array<{\n physicalLocation: {\n artifactLocation: { uri: string }\n region: { startLine: number }\n }\n }>\n}\n\nexport interface SarifLog {\n $schema: string\n version: \"2.1.0\"\n runs: Array<{\n tool: {\n driver: {\n name: string\n version: string\n informationUri: string\n rules: Array<{\n id: string\n helpUri: string\n shortDescription?: { text: string }\n defaultConfiguration?: { level: SarifLevel }\n }>\n }\n }\n results: SarifResult[]\n }>\n}\n\nconst LEVEL: Record<Diagnostic[\"severity\"], SarifLevel> = {\n error: \"error\",\n warning: \"warning\",\n info: \"note\",\n}\n\ntype SarifRule = {\n id: string\n helpUri: string\n shortDescription?: { text: string }\n defaultConfiguration?: { level: SarifLevel }\n}\n\nexport function toSarif(report: Report, opts: SarifOptions): SarifLog {\n const rules = new Map<string, SarifRule>()\n for (const d of report.diagnostics) {\n if (rules.has(d.rule)) continue\n const entry: SarifRule = { id: d.rule, helpUri: d.specUrl }\n const catalog = RULES.get(d.rule)\n if (catalog !== undefined) {\n entry.shortDescription = { text: catalog.title }\n entry.defaultConfiguration = { level: LEVEL[catalog.severity] }\n }\n rules.set(d.rule, entry)\n }\n\n const results: SarifResult[] = report.diagnostics.map((d) => ({\n ruleId: d.rule,\n level: LEVEL[d.severity],\n message: { text: d.message },\n locations:\n opts.artifactUri !== undefined && d.eventIndex >= 0\n ? [\n {\n physicalLocation: {\n artifactLocation: { uri: opts.artifactUri },\n region: { startLine: d.eventIndex + 1 },\n },\n },\n ]\n : [],\n }))\n\n return {\n $schema: \"https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/schemas/sarif-schema-2.1.0.json\",\n version: \"2.1.0\",\n runs: [\n {\n tool: {\n driver: {\n name: \"ag-ui-validate\",\n version: opts.toolVersion,\n informationUri: \"https://github.com/langport-dev/ag-ui-validate\",\n rules: [...rules.values()],\n },\n },\n results,\n },\n ],\n }\n}\n","// JUnit XML output for CI systems. One testcase per finding: errors are\n// <failure>, warnings and info are <skipped> (visible but not build-breaking —\n// the exit code, not the XML, decides pass/fail). A clean report is a single\n// passing testcase so the suite is never empty.\nimport type { Report } from \"../types.js\"\n\nexport interface JUnitOptions {\n /** Suite name — typically the validated target. */\n name: string\n}\n\nfunction esc(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\")\n}\n\nexport function toJUnit(report: Report, opts: JUnitOptions): string {\n const lines: string[] = ['<?xml version=\"1.0\" encoding=\"UTF-8\"?>']\n const suite = esc(opts.name)\n\n if (report.diagnostics.length === 0) {\n lines.push('<testsuites name=\"ag-ui-validate\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">')\n lines.push(` <testsuite name=\"${suite}\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">`)\n lines.push(\n ` <testcase name=\"AG-UI conformance: no violations across ${report.eventCount} events\" classname=\"${suite}\"/>`,\n )\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n }\n\n const failures = report.diagnostics.filter((d) => d.severity === \"error\").length\n const skipped = report.diagnostics.length - failures\n const counts = `tests=\"${report.diagnostics.length}\" failures=\"${failures}\" errors=\"0\" skipped=\"${skipped}\"`\n lines.push(`<testsuites name=\"ag-ui-validate\" ${counts}>`)\n lines.push(` <testsuite name=\"${suite}\" ${counts}>`)\n for (const d of report.diagnostics) {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"stream\"\n const name = esc(`${d.rule} (${where})`)\n lines.push(` <testcase name=\"${name}\" classname=\"${suite}\">`)\n const body = `${esc(d.message)}\\n${esc(d.specUrl)}`\n if (d.severity === \"error\") {\n lines.push(` <failure message=\"${esc(d.message)}\">${body}</failure>`)\n } else {\n lines.push(` <skipped message=\"${esc(`${d.severity}: ${d.message}`)}\">${body}</skipped>`)\n }\n lines.push(\" </testcase>\")\n }\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"report.cjs","names":["RULES","RULES","RULES"],"sources":["../js/src/report/pretty.ts","../js/src/report/json.ts","../js/src/report/sarif.ts","../js/src/report/junit.ts"],"sourcesContent":["// Human-readable output. Pure string formatting — the CLI decides where it\n// goes and whether a TTY wants color.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report, Severity } from \"../types.js\"\n\nexport interface PrettyOptions {\n color: boolean\n}\n\nconst SYMBOL: Record<Severity, string> = { error: \"✖\", warning: \"⚠\", info: \"ℹ\" }\nconst SGR: Record<Severity, string> = { error: \"31\", warning: \"33\", info: \"36\" }\n\nfunction paint(code: string, s: string, on: boolean): string {\n return on ? `\\x1b[${code}m${s}\\x1b[0m` : s\n}\n\nexport function formatDiagnosticLine(d: Diagnostic, opts: PrettyOptions): string {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"—\"\n const head = paint(SGR[d.severity], `${SYMBOL[d.severity]} ${d.rule}`, opts.color)\n const meta = paint(\"2\", `${d.severity.padEnd(7)} ${where.padEnd(10)}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${d.specUrl}`, opts.color)\n return `${head} ${meta} ${d.message}\\n${cite}`\n}\n\nfunction count(n: number, noun: string): string {\n return `${n} ${noun}${n === 1 ? \"\" : \"s\"}`\n}\n\nconst SEVERITY_RANK: Record<Severity, number> = { error: 0, warning: 1, info: 2 }\nconst SAMPLE_INDEXES = 3\n\n/**\n * One line per rule instead of one per occurrence — for large streams where\n * the same violation repeats. Totals stay honest in the summary; this only\n * changes what is listed.\n */\nexport function formatGroupedDiagnostics(diagnostics: Diagnostic[], opts: PrettyOptions): string {\n const groups = new Map<string, Diagnostic[]>()\n for (const d of diagnostics) {\n const list = groups.get(d.rule)\n if (list === undefined) groups.set(d.rule, [d])\n else list.push(d)\n }\n\n const sorted = [...groups.values()].sort((a, b) => {\n const bySeverity = SEVERITY_RANK[a[0]!.severity] - SEVERITY_RANK[b[0]!.severity]\n return bySeverity !== 0 ? bySeverity : a[0]!.rule.localeCompare(b[0]!.rule)\n })\n\n const lines: string[] = []\n for (const group of sorted) {\n const first = group[0]!\n const title = RULES.get(first.rule)?.title ?? first.message\n const indexes = group.map((d) => d.eventIndex).filter((i) => i >= 0)\n let where: string\n if (indexes.length === 0) {\n where = \"stream-level\"\n } else {\n const sample = indexes.slice(0, SAMPLE_INDEXES).join(\", \")\n const rest = indexes.length - SAMPLE_INDEXES\n where = `events ${sample}${rest > 0 ? ` (+${rest} more)` : \"\"}`\n }\n const head = paint(SGR[first.severity], `${SYMBOL[first.severity]} ${first.rule}`, opts.color)\n const meta = paint(\"2\", `${first.severity.padEnd(7)} ×${group.length}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${first.specUrl}`, opts.color)\n lines.push(`${head} ${meta} ${title} — ${where}`, cite)\n }\n return lines.join(\"\\n\")\n}\n\nexport function formatReportSummary(report: Report, opts: PrettyOptions): string {\n const { errors, warnings, info } = report.summary\n const lines: string[] = []\n\n if (errors + warnings + info === 0) {\n lines.push(\n paint(\"32\", `✔ no conformance violations across ${count(report.eventCount, \"event\")}`, opts.color),\n )\n } else {\n lines.push(\n `${count(errors, \"error\")}, ${count(warnings, \"warning\")}, ${info} info across ${count(report.eventCount, \"event\")}`,\n )\n }\n\n const features = Object.entries(report.features)\n const exercised = features.filter(([, s]) => s === \"exercised\").map(([f]) => f)\n const suffix = exercised.length > 0 ? `: ${exercised.join(\", \")}` : \"\"\n lines.push(`${exercised.length} of ${features.length} AG-UI features exercised${suffix}`)\n\n if (report.skipped.length > 0) {\n lines.push(`${count(report.skipped.length, \"rule\")} not evaluated:`)\n for (const s of report.skipped) {\n lines.push(paint(\"2\", ` – ${s.rule}: ${s.reason}`, opts.color))\n }\n }\n\n for (const e of report.internalErrors) {\n lines.push(paint(\"31\", `! internal validator error: ${e}`, opts.color))\n }\n\n return lines.join(\"\\n\")\n}\n","// Machine-readable report: the core Report plus tool identification, so a\n// stored document is self-describing.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report } from \"../types.js\"\n\nexport interface JsonReportOptions {\n tool: { name: string; version: string }\n /** What was validated: URL, file path, or \"stdin\". */\n target?: string\n}\n\nexport interface JsonDiagnostic extends Diagnostic {\n /** The rule's catalog category, e.g. \"toolcall\". Joined in from the catalog by rule id. */\n category?: string\n}\n\nexport interface JsonReportDocument extends Omit<Report, \"diagnostics\"> {\n tool: { name: string; version: string }\n target?: string\n diagnostics: JsonDiagnostic[]\n}\n\nexport function toJsonReport(report: Report, opts: JsonReportOptions): JsonReportDocument {\n const diagnostics: JsonDiagnostic[] = report.diagnostics.map((d) => {\n const category = RULES.get(d.rule)?.category\n return category === undefined ? { ...d } : { ...d, category }\n })\n const doc: JsonReportDocument = { tool: opts.tool, ...report, diagnostics }\n if (opts.target !== undefined) doc.target = opts.target\n return doc\n}\n","// SARIF 2.1.0 output for code-scanning integrations (e.g. GitHub).\n// Level mapping: error→error, warning→warning, info→note. When the input was a\n// line-oriented file (JSONL/captured SSE fed line-per-event is not guaranteed,\n// so only the caller knows), event N is reported at line N+1 via artifactUri.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report } from \"../types.js\"\n\nexport interface SarifOptions {\n toolVersion: string\n /** URI of the validated artifact; enables line-based locations. */\n artifactUri?: string\n}\n\nexport type SarifLevel = \"error\" | \"warning\" | \"note\"\n\nexport interface SarifResult {\n ruleId: string\n level: SarifLevel\n message: { text: string }\n locations: Array<{\n physicalLocation: {\n artifactLocation: { uri: string }\n region: { startLine: number }\n }\n }>\n}\n\nexport interface SarifLog {\n $schema: string\n version: \"2.1.0\"\n runs: Array<{\n tool: {\n driver: {\n name: string\n version: string\n informationUri: string\n rules: SarifRule[]\n }\n }\n results: SarifResult[]\n }>\n}\n\nconst LEVEL: Record<Diagnostic[\"severity\"], SarifLevel> = {\n error: \"error\",\n warning: \"warning\",\n info: \"note\",\n}\n\ntype SarifRule = {\n id: string\n helpUri: string\n shortDescription?: { text: string }\n defaultConfiguration?: { level: SarifLevel }\n properties?: { tags: string[] }\n}\n\nexport function toSarif(report: Report, opts: SarifOptions): SarifLog {\n const rules = new Map<string, SarifRule>()\n for (const d of report.diagnostics) {\n if (rules.has(d.rule)) continue\n const entry: SarifRule = { id: d.rule, helpUri: d.specUrl }\n const catalog = RULES.get(d.rule)\n if (catalog !== undefined) {\n entry.shortDescription = { text: catalog.title }\n entry.defaultConfiguration = { level: LEVEL[catalog.severity] }\n entry.properties = { tags: [catalog.category] }\n }\n rules.set(d.rule, entry)\n }\n\n const results: SarifResult[] = report.diagnostics.map((d) => ({\n ruleId: d.rule,\n level: LEVEL[d.severity],\n message: { text: d.message },\n locations:\n opts.artifactUri !== undefined && d.eventIndex >= 0\n ? [\n {\n physicalLocation: {\n artifactLocation: { uri: opts.artifactUri },\n region: { startLine: d.eventIndex + 1 },\n },\n },\n ]\n : [],\n }))\n\n return {\n $schema: \"https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/schemas/sarif-schema-2.1.0.json\",\n version: \"2.1.0\",\n runs: [\n {\n tool: {\n driver: {\n name: \"ag-ui-validate\",\n version: opts.toolVersion,\n informationUri: \"https://github.com/langport-dev/ag-ui-validate\",\n rules: [...rules.values()],\n },\n },\n results,\n },\n ],\n }\n}\n","// JUnit XML output for CI systems. One testcase per finding: errors are\n// <failure>, warnings and info are <skipped> (visible but not build-breaking —\n// the exit code, not the XML, decides pass/fail). A clean report is a single\n// passing testcase so the suite is never empty.\nimport type { Report } from \"../types.js\"\n\nexport interface JUnitOptions {\n /** Suite name — typically the validated target. */\n name: string\n}\n\nfunction esc(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\")\n}\n\nexport function toJUnit(report: Report, opts: JUnitOptions): string {\n const lines: string[] = ['<?xml version=\"1.0\" encoding=\"UTF-8\"?>']\n const suite = esc(opts.name)\n\n if (report.diagnostics.length === 0) {\n lines.push('<testsuites name=\"ag-ui-validate\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">')\n lines.push(` <testsuite name=\"${suite}\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">`)\n lines.push(\n ` <testcase name=\"AG-UI conformance: no violations across ${report.eventCount} events\" classname=\"${suite}\"/>`,\n )\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n }\n\n const failures = report.diagnostics.filter((d) => d.severity === \"error\").length\n const skipped = report.diagnostics.length - failures\n const counts = `tests=\"${report.diagnostics.length}\" failures=\"${failures}\" errors=\"0\" skipped=\"${skipped}\"`\n lines.push(`<testsuites name=\"ag-ui-validate\" ${counts}>`)\n lines.push(` <testsuite name=\"${suite}\" ${counts}>`)\n for (const d of report.diagnostics) {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"stream\"\n const name = esc(`${d.rule} (${where})`)\n lines.push(` <testcase name=\"${name}\" classname=\"${suite}\">`)\n const body = `${esc(d.message)}\\n${esc(d.specUrl)}`\n if (d.severity === \"error\") {\n lines.push(` <failure message=\"${esc(d.message)}\">${body}</failure>`)\n } else {\n lines.push(` <skipped message=\"${esc(`${d.severity}: ${d.message}`)}\">${body}</skipped>`)\n }\n lines.push(\" </testcase>\")\n }\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n}\n"],"mappings":"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"}
|
package/dist/report.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as Report, r as Diagnostic } from "./types-
|
|
1
|
+
import { o as Report, r as Diagnostic } from "./types-CUIgyj2N.cjs";
|
|
2
2
|
//#region src/report/pretty.d.ts
|
|
3
3
|
interface PrettyOptions {
|
|
4
4
|
color: boolean;
|
|
@@ -21,12 +21,17 @@ interface JsonReportOptions {
|
|
|
21
21
|
/** What was validated: URL, file path, or "stdin". */
|
|
22
22
|
target?: string;
|
|
23
23
|
}
|
|
24
|
-
interface
|
|
24
|
+
interface JsonDiagnostic extends Diagnostic {
|
|
25
|
+
/** The rule's catalog category, e.g. "toolcall". Joined in from the catalog by rule id. */
|
|
26
|
+
category?: string;
|
|
27
|
+
}
|
|
28
|
+
interface JsonReportDocument extends Omit<Report, "diagnostics"> {
|
|
25
29
|
tool: {
|
|
26
30
|
name: string;
|
|
27
31
|
version: string;
|
|
28
32
|
};
|
|
29
33
|
target?: string;
|
|
34
|
+
diagnostics: JsonDiagnostic[];
|
|
30
35
|
}
|
|
31
36
|
declare function toJsonReport(report: Report, opts: JsonReportOptions): JsonReportDocument;
|
|
32
37
|
//#endregion
|
|
@@ -63,21 +68,25 @@ interface SarifLog {
|
|
|
63
68
|
name: string;
|
|
64
69
|
version: string;
|
|
65
70
|
informationUri: string;
|
|
66
|
-
rules:
|
|
67
|
-
id: string;
|
|
68
|
-
helpUri: string;
|
|
69
|
-
shortDescription?: {
|
|
70
|
-
text: string;
|
|
71
|
-
};
|
|
72
|
-
defaultConfiguration?: {
|
|
73
|
-
level: SarifLevel;
|
|
74
|
-
};
|
|
75
|
-
}>;
|
|
71
|
+
rules: SarifRule[];
|
|
76
72
|
};
|
|
77
73
|
};
|
|
78
74
|
results: SarifResult[];
|
|
79
75
|
}>;
|
|
80
76
|
}
|
|
77
|
+
type SarifRule = {
|
|
78
|
+
id: string;
|
|
79
|
+
helpUri: string;
|
|
80
|
+
shortDescription?: {
|
|
81
|
+
text: string;
|
|
82
|
+
};
|
|
83
|
+
defaultConfiguration?: {
|
|
84
|
+
level: SarifLevel;
|
|
85
|
+
};
|
|
86
|
+
properties?: {
|
|
87
|
+
tags: string[];
|
|
88
|
+
};
|
|
89
|
+
};
|
|
81
90
|
declare function toSarif(report: Report, opts: SarifOptions): SarifLog;
|
|
82
91
|
//#endregion
|
|
83
92
|
//#region src/report/junit.d.ts
|
package/dist/report.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as Report, r as Diagnostic } from "./types-
|
|
1
|
+
import { o as Report, r as Diagnostic } from "./types-CUIgyj2N.js";
|
|
2
2
|
//#region src/report/pretty.d.ts
|
|
3
3
|
interface PrettyOptions {
|
|
4
4
|
color: boolean;
|
|
@@ -21,12 +21,17 @@ interface JsonReportOptions {
|
|
|
21
21
|
/** What was validated: URL, file path, or "stdin". */
|
|
22
22
|
target?: string;
|
|
23
23
|
}
|
|
24
|
-
interface
|
|
24
|
+
interface JsonDiagnostic extends Diagnostic {
|
|
25
|
+
/** The rule's catalog category, e.g. "toolcall". Joined in from the catalog by rule id. */
|
|
26
|
+
category?: string;
|
|
27
|
+
}
|
|
28
|
+
interface JsonReportDocument extends Omit<Report, "diagnostics"> {
|
|
25
29
|
tool: {
|
|
26
30
|
name: string;
|
|
27
31
|
version: string;
|
|
28
32
|
};
|
|
29
33
|
target?: string;
|
|
34
|
+
diagnostics: JsonDiagnostic[];
|
|
30
35
|
}
|
|
31
36
|
declare function toJsonReport(report: Report, opts: JsonReportOptions): JsonReportDocument;
|
|
32
37
|
//#endregion
|
|
@@ -63,21 +68,25 @@ interface SarifLog {
|
|
|
63
68
|
name: string;
|
|
64
69
|
version: string;
|
|
65
70
|
informationUri: string;
|
|
66
|
-
rules:
|
|
67
|
-
id: string;
|
|
68
|
-
helpUri: string;
|
|
69
|
-
shortDescription?: {
|
|
70
|
-
text: string;
|
|
71
|
-
};
|
|
72
|
-
defaultConfiguration?: {
|
|
73
|
-
level: SarifLevel;
|
|
74
|
-
};
|
|
75
|
-
}>;
|
|
71
|
+
rules: SarifRule[];
|
|
76
72
|
};
|
|
77
73
|
};
|
|
78
74
|
results: SarifResult[];
|
|
79
75
|
}>;
|
|
80
76
|
}
|
|
77
|
+
type SarifRule = {
|
|
78
|
+
id: string;
|
|
79
|
+
helpUri: string;
|
|
80
|
+
shortDescription?: {
|
|
81
|
+
text: string;
|
|
82
|
+
};
|
|
83
|
+
defaultConfiguration?: {
|
|
84
|
+
level: SarifLevel;
|
|
85
|
+
};
|
|
86
|
+
properties?: {
|
|
87
|
+
tags: string[];
|
|
88
|
+
};
|
|
89
|
+
};
|
|
81
90
|
declare function toSarif(report: Report, opts: SarifOptions): SarifLog;
|
|
82
91
|
//#endregion
|
|
83
92
|
//#region src/report/junit.d.ts
|
package/dist/report.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as RULES } from "./catalog-
|
|
1
|
+
import { n as RULES } from "./catalog-3e6INLCp.js";
|
|
2
2
|
//#region src/report/pretty.ts
|
|
3
3
|
const SYMBOL = {
|
|
4
4
|
error: "✖",
|
|
@@ -83,9 +83,17 @@ function formatReportSummary(report, opts) {
|
|
|
83
83
|
//#endregion
|
|
84
84
|
//#region src/report/json.ts
|
|
85
85
|
function toJsonReport(report, opts) {
|
|
86
|
+
const diagnostics = report.diagnostics.map((d) => {
|
|
87
|
+
const category = RULES.get(d.rule)?.category;
|
|
88
|
+
return category === void 0 ? { ...d } : {
|
|
89
|
+
...d,
|
|
90
|
+
category
|
|
91
|
+
};
|
|
92
|
+
});
|
|
86
93
|
const doc = {
|
|
87
94
|
tool: opts.tool,
|
|
88
|
-
...report
|
|
95
|
+
...report,
|
|
96
|
+
diagnostics
|
|
89
97
|
};
|
|
90
98
|
if (opts.target !== void 0) doc.target = opts.target;
|
|
91
99
|
return doc;
|
|
@@ -109,6 +117,7 @@ function toSarif(report, opts) {
|
|
|
109
117
|
if (catalog !== void 0) {
|
|
110
118
|
entry.shortDescription = { text: catalog.title };
|
|
111
119
|
entry.defaultConfiguration = { level: LEVEL[catalog.severity] };
|
|
120
|
+
entry.properties = { tags: [catalog.category] };
|
|
112
121
|
}
|
|
113
122
|
rules.set(d.rule, entry);
|
|
114
123
|
}
|
package/dist/report.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","names":[],"sources":["../js/src/report/pretty.ts","../js/src/report/json.ts","../js/src/report/sarif.ts","../js/src/report/junit.ts"],"sourcesContent":["// Human-readable output. Pure string formatting — the CLI decides where it\n// goes and whether a TTY wants color.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report, Severity } from \"../types.js\"\n\nexport interface PrettyOptions {\n color: boolean\n}\n\nconst SYMBOL: Record<Severity, string> = { error: \"✖\", warning: \"⚠\", info: \"ℹ\" }\nconst SGR: Record<Severity, string> = { error: \"31\", warning: \"33\", info: \"36\" }\n\nfunction paint(code: string, s: string, on: boolean): string {\n return on ? `\\x1b[${code}m${s}\\x1b[0m` : s\n}\n\nexport function formatDiagnosticLine(d: Diagnostic, opts: PrettyOptions): string {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"—\"\n const head = paint(SGR[d.severity], `${SYMBOL[d.severity]} ${d.rule}`, opts.color)\n const meta = paint(\"2\", `${d.severity.padEnd(7)} ${where.padEnd(10)}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${d.specUrl}`, opts.color)\n return `${head} ${meta} ${d.message}\\n${cite}`\n}\n\nfunction count(n: number, noun: string): string {\n return `${n} ${noun}${n === 1 ? \"\" : \"s\"}`\n}\n\nconst SEVERITY_RANK: Record<Severity, number> = { error: 0, warning: 1, info: 2 }\nconst SAMPLE_INDEXES = 3\n\n/**\n * One line per rule instead of one per occurrence — for large streams where\n * the same violation repeats. Totals stay honest in the summary; this only\n * changes what is listed.\n */\nexport function formatGroupedDiagnostics(diagnostics: Diagnostic[], opts: PrettyOptions): string {\n const groups = new Map<string, Diagnostic[]>()\n for (const d of diagnostics) {\n const list = groups.get(d.rule)\n if (list === undefined) groups.set(d.rule, [d])\n else list.push(d)\n }\n\n const sorted = [...groups.values()].sort((a, b) => {\n const bySeverity = SEVERITY_RANK[a[0]!.severity] - SEVERITY_RANK[b[0]!.severity]\n return bySeverity !== 0 ? bySeverity : a[0]!.rule.localeCompare(b[0]!.rule)\n })\n\n const lines: string[] = []\n for (const group of sorted) {\n const first = group[0]!\n const title = RULES.get(first.rule)?.title ?? first.message\n const indexes = group.map((d) => d.eventIndex).filter((i) => i >= 0)\n let where: string\n if (indexes.length === 0) {\n where = \"stream-level\"\n } else {\n const sample = indexes.slice(0, SAMPLE_INDEXES).join(\", \")\n const rest = indexes.length - SAMPLE_INDEXES\n where = `events ${sample}${rest > 0 ? ` (+${rest} more)` : \"\"}`\n }\n const head = paint(SGR[first.severity], `${SYMBOL[first.severity]} ${first.rule}`, opts.color)\n const meta = paint(\"2\", `${first.severity.padEnd(7)} ×${group.length}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${first.specUrl}`, opts.color)\n lines.push(`${head} ${meta} ${title} — ${where}`, cite)\n }\n return lines.join(\"\\n\")\n}\n\nexport function formatReportSummary(report: Report, opts: PrettyOptions): string {\n const { errors, warnings, info } = report.summary\n const lines: string[] = []\n\n if (errors + warnings + info === 0) {\n lines.push(\n paint(\"32\", `✔ no conformance violations across ${count(report.eventCount, \"event\")}`, opts.color),\n )\n } else {\n lines.push(\n `${count(errors, \"error\")}, ${count(warnings, \"warning\")}, ${info} info across ${count(report.eventCount, \"event\")}`,\n )\n }\n\n const features = Object.entries(report.features)\n const exercised = features.filter(([, s]) => s === \"exercised\").map(([f]) => f)\n const suffix = exercised.length > 0 ? `: ${exercised.join(\", \")}` : \"\"\n lines.push(`${exercised.length} of ${features.length} AG-UI features exercised${suffix}`)\n\n if (report.skipped.length > 0) {\n lines.push(`${count(report.skipped.length, \"rule\")} not evaluated:`)\n for (const s of report.skipped) {\n lines.push(paint(\"2\", ` – ${s.rule}: ${s.reason}`, opts.color))\n }\n }\n\n for (const e of report.internalErrors) {\n lines.push(paint(\"31\", `! internal validator error: ${e}`, opts.color))\n }\n\n return lines.join(\"\\n\")\n}\n","// Machine-readable report: the core Report plus tool identification, so a\n// stored document is self-describing.\nimport type { Report } from \"../types.js\"\n\nexport interface JsonReportOptions {\n tool: { name: string; version: string }\n /** What was validated: URL, file path, or \"stdin\". */\n target?: string\n}\n\nexport interface JsonReportDocument extends Report {\n tool: { name: string; version: string }\n target?: string\n}\n\nexport function toJsonReport(report: Report, opts: JsonReportOptions): JsonReportDocument {\n const doc: JsonReportDocument = { tool: opts.tool, ...report }\n if (opts.target !== undefined) doc.target = opts.target\n return doc\n}\n","// SARIF 2.1.0 output for code-scanning integrations (e.g. GitHub).\n// Level mapping: error→error, warning→warning, info→note. When the input was a\n// line-oriented file (JSONL/captured SSE fed line-per-event is not guaranteed,\n// so only the caller knows), event N is reported at line N+1 via artifactUri.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report } from \"../types.js\"\n\nexport interface SarifOptions {\n toolVersion: string\n /** URI of the validated artifact; enables line-based locations. */\n artifactUri?: string\n}\n\nexport type SarifLevel = \"error\" | \"warning\" | \"note\"\n\nexport interface SarifResult {\n ruleId: string\n level: SarifLevel\n message: { text: string }\n locations: Array<{\n physicalLocation: {\n artifactLocation: { uri: string }\n region: { startLine: number }\n }\n }>\n}\n\nexport interface SarifLog {\n $schema: string\n version: \"2.1.0\"\n runs: Array<{\n tool: {\n driver: {\n name: string\n version: string\n informationUri: string\n rules: Array<{\n id: string\n helpUri: string\n shortDescription?: { text: string }\n defaultConfiguration?: { level: SarifLevel }\n }>\n }\n }\n results: SarifResult[]\n }>\n}\n\nconst LEVEL: Record<Diagnostic[\"severity\"], SarifLevel> = {\n error: \"error\",\n warning: \"warning\",\n info: \"note\",\n}\n\ntype SarifRule = {\n id: string\n helpUri: string\n shortDescription?: { text: string }\n defaultConfiguration?: { level: SarifLevel }\n}\n\nexport function toSarif(report: Report, opts: SarifOptions): SarifLog {\n const rules = new Map<string, SarifRule>()\n for (const d of report.diagnostics) {\n if (rules.has(d.rule)) continue\n const entry: SarifRule = { id: d.rule, helpUri: d.specUrl }\n const catalog = RULES.get(d.rule)\n if (catalog !== undefined) {\n entry.shortDescription = { text: catalog.title }\n entry.defaultConfiguration = { level: LEVEL[catalog.severity] }\n }\n rules.set(d.rule, entry)\n }\n\n const results: SarifResult[] = report.diagnostics.map((d) => ({\n ruleId: d.rule,\n level: LEVEL[d.severity],\n message: { text: d.message },\n locations:\n opts.artifactUri !== undefined && d.eventIndex >= 0\n ? [\n {\n physicalLocation: {\n artifactLocation: { uri: opts.artifactUri },\n region: { startLine: d.eventIndex + 1 },\n },\n },\n ]\n : [],\n }))\n\n return {\n $schema: \"https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/schemas/sarif-schema-2.1.0.json\",\n version: \"2.1.0\",\n runs: [\n {\n tool: {\n driver: {\n name: \"ag-ui-validate\",\n version: opts.toolVersion,\n informationUri: \"https://github.com/langport-dev/ag-ui-validate\",\n rules: [...rules.values()],\n },\n },\n results,\n },\n ],\n }\n}\n","// JUnit XML output for CI systems. One testcase per finding: errors are\n// <failure>, warnings and info are <skipped> (visible but not build-breaking —\n// the exit code, not the XML, decides pass/fail). A clean report is a single\n// passing testcase so the suite is never empty.\nimport type { Report } from \"../types.js\"\n\nexport interface JUnitOptions {\n /** Suite name — typically the validated target. */\n name: string\n}\n\nfunction esc(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\")\n}\n\nexport function toJUnit(report: Report, opts: JUnitOptions): string {\n const lines: string[] = ['<?xml version=\"1.0\" encoding=\"UTF-8\"?>']\n const suite = esc(opts.name)\n\n if (report.diagnostics.length === 0) {\n lines.push('<testsuites name=\"ag-ui-validate\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">')\n lines.push(` <testsuite name=\"${suite}\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">`)\n lines.push(\n ` <testcase name=\"AG-UI conformance: no violations across ${report.eventCount} events\" classname=\"${suite}\"/>`,\n )\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n }\n\n const failures = report.diagnostics.filter((d) => d.severity === \"error\").length\n const skipped = report.diagnostics.length - failures\n const counts = `tests=\"${report.diagnostics.length}\" failures=\"${failures}\" errors=\"0\" skipped=\"${skipped}\"`\n lines.push(`<testsuites name=\"ag-ui-validate\" ${counts}>`)\n lines.push(` <testsuite name=\"${suite}\" ${counts}>`)\n for (const d of report.diagnostics) {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"stream\"\n const name = esc(`${d.rule} (${where})`)\n lines.push(` <testcase name=\"${name}\" classname=\"${suite}\">`)\n const body = `${esc(d.message)}\\n${esc(d.specUrl)}`\n if (d.severity === \"error\") {\n lines.push(` <failure message=\"${esc(d.message)}\">${body}</failure>`)\n } else {\n lines.push(` <skipped message=\"${esc(`${d.severity}: ${d.message}`)}\">${body}</skipped>`)\n }\n lines.push(\" </testcase>\")\n }\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"report.js","names":[],"sources":["../js/src/report/pretty.ts","../js/src/report/json.ts","../js/src/report/sarif.ts","../js/src/report/junit.ts"],"sourcesContent":["// Human-readable output. Pure string formatting — the CLI decides where it\n// goes and whether a TTY wants color.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report, Severity } from \"../types.js\"\n\nexport interface PrettyOptions {\n color: boolean\n}\n\nconst SYMBOL: Record<Severity, string> = { error: \"✖\", warning: \"⚠\", info: \"ℹ\" }\nconst SGR: Record<Severity, string> = { error: \"31\", warning: \"33\", info: \"36\" }\n\nfunction paint(code: string, s: string, on: boolean): string {\n return on ? `\\x1b[${code}m${s}\\x1b[0m` : s\n}\n\nexport function formatDiagnosticLine(d: Diagnostic, opts: PrettyOptions): string {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"—\"\n const head = paint(SGR[d.severity], `${SYMBOL[d.severity]} ${d.rule}`, opts.color)\n const meta = paint(\"2\", `${d.severity.padEnd(7)} ${where.padEnd(10)}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${d.specUrl}`, opts.color)\n return `${head} ${meta} ${d.message}\\n${cite}`\n}\n\nfunction count(n: number, noun: string): string {\n return `${n} ${noun}${n === 1 ? \"\" : \"s\"}`\n}\n\nconst SEVERITY_RANK: Record<Severity, number> = { error: 0, warning: 1, info: 2 }\nconst SAMPLE_INDEXES = 3\n\n/**\n * One line per rule instead of one per occurrence — for large streams where\n * the same violation repeats. Totals stay honest in the summary; this only\n * changes what is listed.\n */\nexport function formatGroupedDiagnostics(diagnostics: Diagnostic[], opts: PrettyOptions): string {\n const groups = new Map<string, Diagnostic[]>()\n for (const d of diagnostics) {\n const list = groups.get(d.rule)\n if (list === undefined) groups.set(d.rule, [d])\n else list.push(d)\n }\n\n const sorted = [...groups.values()].sort((a, b) => {\n const bySeverity = SEVERITY_RANK[a[0]!.severity] - SEVERITY_RANK[b[0]!.severity]\n return bySeverity !== 0 ? bySeverity : a[0]!.rule.localeCompare(b[0]!.rule)\n })\n\n const lines: string[] = []\n for (const group of sorted) {\n const first = group[0]!\n const title = RULES.get(first.rule)?.title ?? first.message\n const indexes = group.map((d) => d.eventIndex).filter((i) => i >= 0)\n let where: string\n if (indexes.length === 0) {\n where = \"stream-level\"\n } else {\n const sample = indexes.slice(0, SAMPLE_INDEXES).join(\", \")\n const rest = indexes.length - SAMPLE_INDEXES\n where = `events ${sample}${rest > 0 ? ` (+${rest} more)` : \"\"}`\n }\n const head = paint(SGR[first.severity], `${SYMBOL[first.severity]} ${first.rule}`, opts.color)\n const meta = paint(\"2\", `${first.severity.padEnd(7)} ×${group.length}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${first.specUrl}`, opts.color)\n lines.push(`${head} ${meta} ${title} — ${where}`, cite)\n }\n return lines.join(\"\\n\")\n}\n\nexport function formatReportSummary(report: Report, opts: PrettyOptions): string {\n const { errors, warnings, info } = report.summary\n const lines: string[] = []\n\n if (errors + warnings + info === 0) {\n lines.push(\n paint(\"32\", `✔ no conformance violations across ${count(report.eventCount, \"event\")}`, opts.color),\n )\n } else {\n lines.push(\n `${count(errors, \"error\")}, ${count(warnings, \"warning\")}, ${info} info across ${count(report.eventCount, \"event\")}`,\n )\n }\n\n const features = Object.entries(report.features)\n const exercised = features.filter(([, s]) => s === \"exercised\").map(([f]) => f)\n const suffix = exercised.length > 0 ? `: ${exercised.join(\", \")}` : \"\"\n lines.push(`${exercised.length} of ${features.length} AG-UI features exercised${suffix}`)\n\n if (report.skipped.length > 0) {\n lines.push(`${count(report.skipped.length, \"rule\")} not evaluated:`)\n for (const s of report.skipped) {\n lines.push(paint(\"2\", ` – ${s.rule}: ${s.reason}`, opts.color))\n }\n }\n\n for (const e of report.internalErrors) {\n lines.push(paint(\"31\", `! internal validator error: ${e}`, opts.color))\n }\n\n return lines.join(\"\\n\")\n}\n","// Machine-readable report: the core Report plus tool identification, so a\n// stored document is self-describing.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report } from \"../types.js\"\n\nexport interface JsonReportOptions {\n tool: { name: string; version: string }\n /** What was validated: URL, file path, or \"stdin\". */\n target?: string\n}\n\nexport interface JsonDiagnostic extends Diagnostic {\n /** The rule's catalog category, e.g. \"toolcall\". Joined in from the catalog by rule id. */\n category?: string\n}\n\nexport interface JsonReportDocument extends Omit<Report, \"diagnostics\"> {\n tool: { name: string; version: string }\n target?: string\n diagnostics: JsonDiagnostic[]\n}\n\nexport function toJsonReport(report: Report, opts: JsonReportOptions): JsonReportDocument {\n const diagnostics: JsonDiagnostic[] = report.diagnostics.map((d) => {\n const category = RULES.get(d.rule)?.category\n return category === undefined ? { ...d } : { ...d, category }\n })\n const doc: JsonReportDocument = { tool: opts.tool, ...report, diagnostics }\n if (opts.target !== undefined) doc.target = opts.target\n return doc\n}\n","// SARIF 2.1.0 output for code-scanning integrations (e.g. GitHub).\n// Level mapping: error→error, warning→warning, info→note. When the input was a\n// line-oriented file (JSONL/captured SSE fed line-per-event is not guaranteed,\n// so only the caller knows), event N is reported at line N+1 via artifactUri.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report } from \"../types.js\"\n\nexport interface SarifOptions {\n toolVersion: string\n /** URI of the validated artifact; enables line-based locations. */\n artifactUri?: string\n}\n\nexport type SarifLevel = \"error\" | \"warning\" | \"note\"\n\nexport interface SarifResult {\n ruleId: string\n level: SarifLevel\n message: { text: string }\n locations: Array<{\n physicalLocation: {\n artifactLocation: { uri: string }\n region: { startLine: number }\n }\n }>\n}\n\nexport interface SarifLog {\n $schema: string\n version: \"2.1.0\"\n runs: Array<{\n tool: {\n driver: {\n name: string\n version: string\n informationUri: string\n rules: SarifRule[]\n }\n }\n results: SarifResult[]\n }>\n}\n\nconst LEVEL: Record<Diagnostic[\"severity\"], SarifLevel> = {\n error: \"error\",\n warning: \"warning\",\n info: \"note\",\n}\n\ntype SarifRule = {\n id: string\n helpUri: string\n shortDescription?: { text: string }\n defaultConfiguration?: { level: SarifLevel }\n properties?: { tags: string[] }\n}\n\nexport function toSarif(report: Report, opts: SarifOptions): SarifLog {\n const rules = new Map<string, SarifRule>()\n for (const d of report.diagnostics) {\n if (rules.has(d.rule)) continue\n const entry: SarifRule = { id: d.rule, helpUri: d.specUrl }\n const catalog = RULES.get(d.rule)\n if (catalog !== undefined) {\n entry.shortDescription = { text: catalog.title }\n entry.defaultConfiguration = { level: LEVEL[catalog.severity] }\n entry.properties = { tags: [catalog.category] }\n }\n rules.set(d.rule, entry)\n }\n\n const results: SarifResult[] = report.diagnostics.map((d) => ({\n ruleId: d.rule,\n level: LEVEL[d.severity],\n message: { text: d.message },\n locations:\n opts.artifactUri !== undefined && d.eventIndex >= 0\n ? [\n {\n physicalLocation: {\n artifactLocation: { uri: opts.artifactUri },\n region: { startLine: d.eventIndex + 1 },\n },\n },\n ]\n : [],\n }))\n\n return {\n $schema: \"https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/schemas/sarif-schema-2.1.0.json\",\n version: \"2.1.0\",\n runs: [\n {\n tool: {\n driver: {\n name: \"ag-ui-validate\",\n version: opts.toolVersion,\n informationUri: \"https://github.com/langport-dev/ag-ui-validate\",\n rules: [...rules.values()],\n },\n },\n results,\n },\n ],\n }\n}\n","// JUnit XML output for CI systems. One testcase per finding: errors are\n// <failure>, warnings and info are <skipped> (visible but not build-breaking —\n// the exit code, not the XML, decides pass/fail). A clean report is a single\n// passing testcase so the suite is never empty.\nimport type { Report } from \"../types.js\"\n\nexport interface JUnitOptions {\n /** Suite name — typically the validated target. */\n name: string\n}\n\nfunction esc(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\")\n}\n\nexport function toJUnit(report: Report, opts: JUnitOptions): string {\n const lines: string[] = ['<?xml version=\"1.0\" encoding=\"UTF-8\"?>']\n const suite = esc(opts.name)\n\n if (report.diagnostics.length === 0) {\n lines.push('<testsuites name=\"ag-ui-validate\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">')\n lines.push(` <testsuite name=\"${suite}\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">`)\n lines.push(\n ` <testcase name=\"AG-UI conformance: no violations across ${report.eventCount} events\" classname=\"${suite}\"/>`,\n )\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n }\n\n const failures = report.diagnostics.filter((d) => d.severity === \"error\").length\n const skipped = report.diagnostics.length - failures\n const counts = `tests=\"${report.diagnostics.length}\" failures=\"${failures}\" errors=\"0\" skipped=\"${skipped}\"`\n lines.push(`<testsuites name=\"ag-ui-validate\" ${counts}>`)\n lines.push(` <testsuite name=\"${suite}\" ${counts}>`)\n for (const d of report.diagnostics) {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"stream\"\n const name = esc(`${d.rule} (${where})`)\n lines.push(` <testcase name=\"${name}\" classname=\"${suite}\">`)\n const body = `${esc(d.message)}\\n${esc(d.specUrl)}`\n if (d.severity === \"error\") {\n lines.push(` <failure message=\"${esc(d.message)}\">${body}</failure>`)\n } else {\n lines.push(` <skipped message=\"${esc(`${d.severity}: ${d.message}`)}\">${body}</skipped>`)\n }\n lines.push(\" </testcase>\")\n }\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n}\n"],"mappings":"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"}
|