ag-ui-validate 0.1.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/LICENSE +21 -0
- package/README.md +229 -0
- package/dist/catalog-BglXBNbL.js +472 -0
- package/dist/catalog-BglXBNbL.js.map +1 -0
- package/dist/catalog-Ci9dqc1a.cjs +495 -0
- package/dist/catalog-Ci9dqc1a.cjs.map +1 -0
- package/dist/cli.js +2783 -0
- package/dist/cli.js.map +1 -0
- package/dist/index-Hmqj3r_r.d.cts +52 -0
- package/dist/index-oNG1kOp9.d.ts +52 -0
- package/dist/index.cjs +14 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/report.cjs +139 -0
- package/dist/report.cjs.map +1 -0
- package/dist/report.d.cts +85 -0
- package/dist/report.d.ts +85 -0
- package/dist/report.js +134 -0
- package/dist/report.js.map +1 -0
- package/dist/src-HmI-kxef.cjs +1596 -0
- package/dist/src-HmI-kxef.cjs.map +1 -0
- package/dist/src-rGZ2G4qA.js +1555 -0
- package/dist/src-rGZ2G4qA.js.map +1 -0
- package/dist/transport.cjs +329 -0
- package/dist/transport.cjs.map +1 -0
- package/dist/transport.d.cts +89 -0
- package/dist/transport.d.ts +89 -0
- package/dist/transport.js +323 -0
- package/dist/transport.js.map +1 -0
- package/dist/types-oH_QTnn2.d.cts +148 -0
- package/dist/types-oH_QTnn2.d.ts +148 -0
- package/dist/vitest.d.ts +28 -0
- package/dist/vitest.js +2089 -0
- package/dist/vitest.js.map +1 -0
- package/package.json +127 -0
- package/src/cli-args.ts +202 -0
- package/src/cli.ts +147 -0
- package/src/index.ts +465 -0
- package/src/protocol/event-table.ts +316 -0
- package/src/protocol/jsonpatch.ts +220 -0
- package/src/report/index.ts +10 -0
- package/src/report/json.ts +20 -0
- package/src/report/junit.ts +56 -0
- package/src/report/pretty.ts +59 -0
- package/src/report/sarif.ts +109 -0
- package/src/rules/catalog.json +431 -0
- package/src/rules/catalog.ts +84 -0
- package/src/rules/checks/context.ts +117 -0
- package/src/rules/checks/lifecycle.ts +59 -0
- package/src/rules/checks/reasoning.ts +97 -0
- package/src/rules/checks/state.ts +72 -0
- package/src/rules/checks/text.ts +109 -0
- package/src/rules/checks/toolcalls.ts +167 -0
- package/src/rules/checks/transport.ts +17 -0
- package/src/transport/index.ts +331 -0
- package/src/transport/ndjson.ts +25 -0
- package/src/transport/sse.ts +126 -0
- package/src/types.ts +136 -0
- package/src/vitest/index.ts +19 -0
- package/src/vitest/matcher.ts +77 -0
package/dist/report.cjs
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_catalog = require("./catalog-Ci9dqc1a.cjs");
|
|
3
|
+
//#region src/report/pretty.ts
|
|
4
|
+
const SYMBOL = {
|
|
5
|
+
error: "✖",
|
|
6
|
+
warning: "⚠",
|
|
7
|
+
info: "ℹ"
|
|
8
|
+
};
|
|
9
|
+
const SGR = {
|
|
10
|
+
error: "31",
|
|
11
|
+
warning: "33",
|
|
12
|
+
info: "36"
|
|
13
|
+
};
|
|
14
|
+
function paint(code, s, on) {
|
|
15
|
+
return on ? `\x1b[${code}m${s}\x1b[0m` : s;
|
|
16
|
+
}
|
|
17
|
+
function formatDiagnosticLine(d, opts) {
|
|
18
|
+
const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : "—";
|
|
19
|
+
const head = paint(SGR[d.severity], `${SYMBOL[d.severity]} ${d.rule}`, opts.color);
|
|
20
|
+
const meta = paint("2", `${d.severity.padEnd(7)} ${where.padEnd(10)}`, opts.color);
|
|
21
|
+
const cite = paint("2", ` ↳ ${d.specUrl}`, opts.color);
|
|
22
|
+
return `${head} ${meta} ${d.message}\n${cite}`;
|
|
23
|
+
}
|
|
24
|
+
function count(n, noun) {
|
|
25
|
+
return `${n} ${noun}${n === 1 ? "" : "s"}`;
|
|
26
|
+
}
|
|
27
|
+
function formatReportSummary(report, opts) {
|
|
28
|
+
const { errors, warnings, info } = report.summary;
|
|
29
|
+
const lines = [];
|
|
30
|
+
if (errors + warnings + info === 0) lines.push(paint("32", `✔ no conformance violations across ${count(report.eventCount, "event")}`, opts.color));
|
|
31
|
+
else lines.push(`${count(errors, "error")}, ${count(warnings, "warning")}, ${info} info across ${count(report.eventCount, "event")}`);
|
|
32
|
+
const features = Object.entries(report.features);
|
|
33
|
+
const exercised = features.filter(([, s]) => s === "exercised").map(([f]) => f);
|
|
34
|
+
const suffix = exercised.length > 0 ? `: ${exercised.join(", ")}` : "";
|
|
35
|
+
lines.push(`${exercised.length} of ${features.length} AG-UI features exercised${suffix}`);
|
|
36
|
+
if (report.skipped.length > 0) {
|
|
37
|
+
lines.push(`${count(report.skipped.length, "rule")} not evaluated:`);
|
|
38
|
+
for (const s of report.skipped) lines.push(paint("2", ` – ${s.rule}: ${s.reason}`, opts.color));
|
|
39
|
+
}
|
|
40
|
+
for (const e of report.internalErrors) lines.push(paint("31", `! internal validator error: ${e}`, opts.color));
|
|
41
|
+
return lines.join("\n");
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/report/json.ts
|
|
45
|
+
function toJsonReport(report, opts) {
|
|
46
|
+
const doc = {
|
|
47
|
+
tool: opts.tool,
|
|
48
|
+
...report
|
|
49
|
+
};
|
|
50
|
+
if (opts.target !== void 0) doc.target = opts.target;
|
|
51
|
+
return doc;
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/report/sarif.ts
|
|
55
|
+
const LEVEL = {
|
|
56
|
+
error: "error",
|
|
57
|
+
warning: "warning",
|
|
58
|
+
info: "note"
|
|
59
|
+
};
|
|
60
|
+
function toSarif(report, opts) {
|
|
61
|
+
const rules = /* @__PURE__ */ new Map();
|
|
62
|
+
for (const d of report.diagnostics) {
|
|
63
|
+
if (rules.has(d.rule)) continue;
|
|
64
|
+
const entry = {
|
|
65
|
+
id: d.rule,
|
|
66
|
+
helpUri: d.specUrl
|
|
67
|
+
};
|
|
68
|
+
const catalog = require_catalog.RULES.get(d.rule);
|
|
69
|
+
if (catalog !== void 0) {
|
|
70
|
+
entry.shortDescription = { text: catalog.title };
|
|
71
|
+
entry.defaultConfiguration = { level: LEVEL[catalog.severity] };
|
|
72
|
+
}
|
|
73
|
+
rules.set(d.rule, entry);
|
|
74
|
+
}
|
|
75
|
+
const results = report.diagnostics.map((d) => ({
|
|
76
|
+
ruleId: d.rule,
|
|
77
|
+
level: LEVEL[d.severity],
|
|
78
|
+
message: { text: d.message },
|
|
79
|
+
locations: opts.artifactUri !== void 0 && d.eventIndex >= 0 ? [{ physicalLocation: {
|
|
80
|
+
artifactLocation: { uri: opts.artifactUri },
|
|
81
|
+
region: { startLine: d.eventIndex + 1 }
|
|
82
|
+
} }] : []
|
|
83
|
+
}));
|
|
84
|
+
return {
|
|
85
|
+
$schema: "https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/schemas/sarif-schema-2.1.0.json",
|
|
86
|
+
version: "2.1.0",
|
|
87
|
+
runs: [{
|
|
88
|
+
tool: { driver: {
|
|
89
|
+
name: "ag-ui-validate",
|
|
90
|
+
version: opts.toolVersion,
|
|
91
|
+
informationUri: "https://github.com/langport-dev/ag-ui-validate",
|
|
92
|
+
rules: [...rules.values()]
|
|
93
|
+
} },
|
|
94
|
+
results
|
|
95
|
+
}]
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/report/junit.ts
|
|
100
|
+
function esc(s) {
|
|
101
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
102
|
+
}
|
|
103
|
+
function toJUnit(report, opts) {
|
|
104
|
+
const lines = ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>"];
|
|
105
|
+
const suite = esc(opts.name);
|
|
106
|
+
if (report.diagnostics.length === 0) {
|
|
107
|
+
lines.push("<testsuites name=\"ag-ui-validate\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">");
|
|
108
|
+
lines.push(` <testsuite name="${suite}" tests="1" failures="0" errors="0" skipped="0">`);
|
|
109
|
+
lines.push(` <testcase name="AG-UI conformance: no violations across ${report.eventCount} events" classname="${suite}"/>`);
|
|
110
|
+
lines.push(" </testsuite>");
|
|
111
|
+
lines.push("</testsuites>");
|
|
112
|
+
return `${lines.join("\n")}\n`;
|
|
113
|
+
}
|
|
114
|
+
const failures = report.diagnostics.filter((d) => d.severity === "error").length;
|
|
115
|
+
const skipped = report.diagnostics.length - failures;
|
|
116
|
+
const counts = `tests="${report.diagnostics.length}" failures="${failures}" errors="0" skipped="${skipped}"`;
|
|
117
|
+
lines.push(`<testsuites name="ag-ui-validate" ${counts}>`);
|
|
118
|
+
lines.push(` <testsuite name="${suite}" ${counts}>`);
|
|
119
|
+
for (const d of report.diagnostics) {
|
|
120
|
+
const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : "stream";
|
|
121
|
+
const name = esc(`${d.rule} (${where})`);
|
|
122
|
+
lines.push(` <testcase name="${name}" classname="${suite}">`);
|
|
123
|
+
const body = `${esc(d.message)}\n${esc(d.specUrl)}`;
|
|
124
|
+
if (d.severity === "error") lines.push(` <failure message="${esc(d.message)}">${body}</failure>`);
|
|
125
|
+
else lines.push(` <skipped message="${esc(`${d.severity}: ${d.message}`)}">${body}</skipped>`);
|
|
126
|
+
lines.push(" </testcase>");
|
|
127
|
+
}
|
|
128
|
+
lines.push(" </testsuite>");
|
|
129
|
+
lines.push("</testsuites>");
|
|
130
|
+
return `${lines.join("\n")}\n`;
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
exports.formatDiagnosticLine = formatDiagnosticLine;
|
|
134
|
+
exports.formatReportSummary = formatReportSummary;
|
|
135
|
+
exports.toJUnit = toJUnit;
|
|
136
|
+
exports.toJsonReport = toJsonReport;
|
|
137
|
+
exports.toSarif = toSarif;
|
|
138
|
+
|
|
139
|
+
//# sourceMappingURL=report.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.cjs","names":["RULES"],"sources":["../src/report/pretty.ts","../src/report/json.ts","../src/report/sarif.ts","../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 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\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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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { o as Report, r as Diagnostic } from "./types-oH_QTnn2.cjs";
|
|
2
|
+
//#region src/report/pretty.d.ts
|
|
3
|
+
interface PrettyOptions {
|
|
4
|
+
color: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare function formatDiagnosticLine(d: Diagnostic, opts: PrettyOptions): string;
|
|
7
|
+
declare function formatReportSummary(report: Report, opts: PrettyOptions): string;
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/report/json.d.ts
|
|
10
|
+
interface JsonReportOptions {
|
|
11
|
+
tool: {
|
|
12
|
+
name: string;
|
|
13
|
+
version: string;
|
|
14
|
+
};
|
|
15
|
+
/** What was validated: URL, file path, or "stdin". */
|
|
16
|
+
target?: string;
|
|
17
|
+
}
|
|
18
|
+
interface JsonReportDocument extends Report {
|
|
19
|
+
tool: {
|
|
20
|
+
name: string;
|
|
21
|
+
version: string;
|
|
22
|
+
};
|
|
23
|
+
target?: string;
|
|
24
|
+
}
|
|
25
|
+
declare function toJsonReport(report: Report, opts: JsonReportOptions): JsonReportDocument;
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/report/sarif.d.ts
|
|
28
|
+
interface SarifOptions {
|
|
29
|
+
toolVersion: string;
|
|
30
|
+
/** URI of the validated artifact; enables line-based locations. */
|
|
31
|
+
artifactUri?: string;
|
|
32
|
+
}
|
|
33
|
+
type SarifLevel = "error" | "warning" | "note";
|
|
34
|
+
interface SarifResult {
|
|
35
|
+
ruleId: string;
|
|
36
|
+
level: SarifLevel;
|
|
37
|
+
message: {
|
|
38
|
+
text: string;
|
|
39
|
+
};
|
|
40
|
+
locations: Array<{
|
|
41
|
+
physicalLocation: {
|
|
42
|
+
artifactLocation: {
|
|
43
|
+
uri: string;
|
|
44
|
+
};
|
|
45
|
+
region: {
|
|
46
|
+
startLine: number;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
}>;
|
|
50
|
+
}
|
|
51
|
+
interface SarifLog {
|
|
52
|
+
$schema: string;
|
|
53
|
+
version: "2.1.0";
|
|
54
|
+
runs: Array<{
|
|
55
|
+
tool: {
|
|
56
|
+
driver: {
|
|
57
|
+
name: string;
|
|
58
|
+
version: string;
|
|
59
|
+
informationUri: string;
|
|
60
|
+
rules: Array<{
|
|
61
|
+
id: string;
|
|
62
|
+
helpUri: string;
|
|
63
|
+
shortDescription?: {
|
|
64
|
+
text: string;
|
|
65
|
+
};
|
|
66
|
+
defaultConfiguration?: {
|
|
67
|
+
level: SarifLevel;
|
|
68
|
+
};
|
|
69
|
+
}>;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
results: SarifResult[];
|
|
73
|
+
}>;
|
|
74
|
+
}
|
|
75
|
+
declare function toSarif(report: Report, opts: SarifOptions): SarifLog;
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/report/junit.d.ts
|
|
78
|
+
interface JUnitOptions {
|
|
79
|
+
/** Suite name — typically the validated target. */
|
|
80
|
+
name: string;
|
|
81
|
+
}
|
|
82
|
+
declare function toJUnit(report: Report, opts: JUnitOptions): string;
|
|
83
|
+
//#endregion
|
|
84
|
+
export { type JUnitOptions, type JsonReportDocument, type JsonReportOptions, type PrettyOptions, type SarifLevel, type SarifLog, type SarifOptions, type SarifResult, formatDiagnosticLine, formatReportSummary, toJUnit, toJsonReport, toSarif };
|
|
85
|
+
//# sourceMappingURL=report.d.cts.map
|
package/dist/report.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { o as Report, r as Diagnostic } from "./types-oH_QTnn2.js";
|
|
2
|
+
//#region src/report/pretty.d.ts
|
|
3
|
+
interface PrettyOptions {
|
|
4
|
+
color: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare function formatDiagnosticLine(d: Diagnostic, opts: PrettyOptions): string;
|
|
7
|
+
declare function formatReportSummary(report: Report, opts: PrettyOptions): string;
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/report/json.d.ts
|
|
10
|
+
interface JsonReportOptions {
|
|
11
|
+
tool: {
|
|
12
|
+
name: string;
|
|
13
|
+
version: string;
|
|
14
|
+
};
|
|
15
|
+
/** What was validated: URL, file path, or "stdin". */
|
|
16
|
+
target?: string;
|
|
17
|
+
}
|
|
18
|
+
interface JsonReportDocument extends Report {
|
|
19
|
+
tool: {
|
|
20
|
+
name: string;
|
|
21
|
+
version: string;
|
|
22
|
+
};
|
|
23
|
+
target?: string;
|
|
24
|
+
}
|
|
25
|
+
declare function toJsonReport(report: Report, opts: JsonReportOptions): JsonReportDocument;
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/report/sarif.d.ts
|
|
28
|
+
interface SarifOptions {
|
|
29
|
+
toolVersion: string;
|
|
30
|
+
/** URI of the validated artifact; enables line-based locations. */
|
|
31
|
+
artifactUri?: string;
|
|
32
|
+
}
|
|
33
|
+
type SarifLevel = "error" | "warning" | "note";
|
|
34
|
+
interface SarifResult {
|
|
35
|
+
ruleId: string;
|
|
36
|
+
level: SarifLevel;
|
|
37
|
+
message: {
|
|
38
|
+
text: string;
|
|
39
|
+
};
|
|
40
|
+
locations: Array<{
|
|
41
|
+
physicalLocation: {
|
|
42
|
+
artifactLocation: {
|
|
43
|
+
uri: string;
|
|
44
|
+
};
|
|
45
|
+
region: {
|
|
46
|
+
startLine: number;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
}>;
|
|
50
|
+
}
|
|
51
|
+
interface SarifLog {
|
|
52
|
+
$schema: string;
|
|
53
|
+
version: "2.1.0";
|
|
54
|
+
runs: Array<{
|
|
55
|
+
tool: {
|
|
56
|
+
driver: {
|
|
57
|
+
name: string;
|
|
58
|
+
version: string;
|
|
59
|
+
informationUri: string;
|
|
60
|
+
rules: Array<{
|
|
61
|
+
id: string;
|
|
62
|
+
helpUri: string;
|
|
63
|
+
shortDescription?: {
|
|
64
|
+
text: string;
|
|
65
|
+
};
|
|
66
|
+
defaultConfiguration?: {
|
|
67
|
+
level: SarifLevel;
|
|
68
|
+
};
|
|
69
|
+
}>;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
results: SarifResult[];
|
|
73
|
+
}>;
|
|
74
|
+
}
|
|
75
|
+
declare function toSarif(report: Report, opts: SarifOptions): SarifLog;
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/report/junit.d.ts
|
|
78
|
+
interface JUnitOptions {
|
|
79
|
+
/** Suite name — typically the validated target. */
|
|
80
|
+
name: string;
|
|
81
|
+
}
|
|
82
|
+
declare function toJUnit(report: Report, opts: JUnitOptions): string;
|
|
83
|
+
//#endregion
|
|
84
|
+
export { type JUnitOptions, type JsonReportDocument, type JsonReportOptions, type PrettyOptions, type SarifLevel, type SarifLog, type SarifOptions, type SarifResult, formatDiagnosticLine, formatReportSummary, toJUnit, toJsonReport, toSarif };
|
|
85
|
+
//# sourceMappingURL=report.d.ts.map
|
package/dist/report.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { n as RULES } from "./catalog-BglXBNbL.js";
|
|
2
|
+
//#region src/report/pretty.ts
|
|
3
|
+
const SYMBOL = {
|
|
4
|
+
error: "✖",
|
|
5
|
+
warning: "⚠",
|
|
6
|
+
info: "ℹ"
|
|
7
|
+
};
|
|
8
|
+
const SGR = {
|
|
9
|
+
error: "31",
|
|
10
|
+
warning: "33",
|
|
11
|
+
info: "36"
|
|
12
|
+
};
|
|
13
|
+
function paint(code, s, on) {
|
|
14
|
+
return on ? `\x1b[${code}m${s}\x1b[0m` : s;
|
|
15
|
+
}
|
|
16
|
+
function formatDiagnosticLine(d, opts) {
|
|
17
|
+
const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : "—";
|
|
18
|
+
const head = paint(SGR[d.severity], `${SYMBOL[d.severity]} ${d.rule}`, opts.color);
|
|
19
|
+
const meta = paint("2", `${d.severity.padEnd(7)} ${where.padEnd(10)}`, opts.color);
|
|
20
|
+
const cite = paint("2", ` ↳ ${d.specUrl}`, opts.color);
|
|
21
|
+
return `${head} ${meta} ${d.message}\n${cite}`;
|
|
22
|
+
}
|
|
23
|
+
function count(n, noun) {
|
|
24
|
+
return `${n} ${noun}${n === 1 ? "" : "s"}`;
|
|
25
|
+
}
|
|
26
|
+
function formatReportSummary(report, opts) {
|
|
27
|
+
const { errors, warnings, info } = report.summary;
|
|
28
|
+
const lines = [];
|
|
29
|
+
if (errors + warnings + info === 0) lines.push(paint("32", `✔ no conformance violations across ${count(report.eventCount, "event")}`, opts.color));
|
|
30
|
+
else lines.push(`${count(errors, "error")}, ${count(warnings, "warning")}, ${info} info across ${count(report.eventCount, "event")}`);
|
|
31
|
+
const features = Object.entries(report.features);
|
|
32
|
+
const exercised = features.filter(([, s]) => s === "exercised").map(([f]) => f);
|
|
33
|
+
const suffix = exercised.length > 0 ? `: ${exercised.join(", ")}` : "";
|
|
34
|
+
lines.push(`${exercised.length} of ${features.length} AG-UI features exercised${suffix}`);
|
|
35
|
+
if (report.skipped.length > 0) {
|
|
36
|
+
lines.push(`${count(report.skipped.length, "rule")} not evaluated:`);
|
|
37
|
+
for (const s of report.skipped) lines.push(paint("2", ` – ${s.rule}: ${s.reason}`, opts.color));
|
|
38
|
+
}
|
|
39
|
+
for (const e of report.internalErrors) lines.push(paint("31", `! internal validator error: ${e}`, opts.color));
|
|
40
|
+
return lines.join("\n");
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/report/json.ts
|
|
44
|
+
function toJsonReport(report, opts) {
|
|
45
|
+
const doc = {
|
|
46
|
+
tool: opts.tool,
|
|
47
|
+
...report
|
|
48
|
+
};
|
|
49
|
+
if (opts.target !== void 0) doc.target = opts.target;
|
|
50
|
+
return doc;
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/report/sarif.ts
|
|
54
|
+
const LEVEL = {
|
|
55
|
+
error: "error",
|
|
56
|
+
warning: "warning",
|
|
57
|
+
info: "note"
|
|
58
|
+
};
|
|
59
|
+
function toSarif(report, opts) {
|
|
60
|
+
const rules = /* @__PURE__ */ new Map();
|
|
61
|
+
for (const d of report.diagnostics) {
|
|
62
|
+
if (rules.has(d.rule)) continue;
|
|
63
|
+
const entry = {
|
|
64
|
+
id: d.rule,
|
|
65
|
+
helpUri: d.specUrl
|
|
66
|
+
};
|
|
67
|
+
const catalog = RULES.get(d.rule);
|
|
68
|
+
if (catalog !== void 0) {
|
|
69
|
+
entry.shortDescription = { text: catalog.title };
|
|
70
|
+
entry.defaultConfiguration = { level: LEVEL[catalog.severity] };
|
|
71
|
+
}
|
|
72
|
+
rules.set(d.rule, entry);
|
|
73
|
+
}
|
|
74
|
+
const results = report.diagnostics.map((d) => ({
|
|
75
|
+
ruleId: d.rule,
|
|
76
|
+
level: LEVEL[d.severity],
|
|
77
|
+
message: { text: d.message },
|
|
78
|
+
locations: opts.artifactUri !== void 0 && d.eventIndex >= 0 ? [{ physicalLocation: {
|
|
79
|
+
artifactLocation: { uri: opts.artifactUri },
|
|
80
|
+
region: { startLine: d.eventIndex + 1 }
|
|
81
|
+
} }] : []
|
|
82
|
+
}));
|
|
83
|
+
return {
|
|
84
|
+
$schema: "https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/schemas/sarif-schema-2.1.0.json",
|
|
85
|
+
version: "2.1.0",
|
|
86
|
+
runs: [{
|
|
87
|
+
tool: { driver: {
|
|
88
|
+
name: "ag-ui-validate",
|
|
89
|
+
version: opts.toolVersion,
|
|
90
|
+
informationUri: "https://github.com/langport-dev/ag-ui-validate",
|
|
91
|
+
rules: [...rules.values()]
|
|
92
|
+
} },
|
|
93
|
+
results
|
|
94
|
+
}]
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/report/junit.ts
|
|
99
|
+
function esc(s) {
|
|
100
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
101
|
+
}
|
|
102
|
+
function toJUnit(report, opts) {
|
|
103
|
+
const lines = ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>"];
|
|
104
|
+
const suite = esc(opts.name);
|
|
105
|
+
if (report.diagnostics.length === 0) {
|
|
106
|
+
lines.push("<testsuites name=\"ag-ui-validate\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">");
|
|
107
|
+
lines.push(` <testsuite name="${suite}" tests="1" failures="0" errors="0" skipped="0">`);
|
|
108
|
+
lines.push(` <testcase name="AG-UI conformance: no violations across ${report.eventCount} events" classname="${suite}"/>`);
|
|
109
|
+
lines.push(" </testsuite>");
|
|
110
|
+
lines.push("</testsuites>");
|
|
111
|
+
return `${lines.join("\n")}\n`;
|
|
112
|
+
}
|
|
113
|
+
const failures = report.diagnostics.filter((d) => d.severity === "error").length;
|
|
114
|
+
const skipped = report.diagnostics.length - failures;
|
|
115
|
+
const counts = `tests="${report.diagnostics.length}" failures="${failures}" errors="0" skipped="${skipped}"`;
|
|
116
|
+
lines.push(`<testsuites name="ag-ui-validate" ${counts}>`);
|
|
117
|
+
lines.push(` <testsuite name="${suite}" ${counts}>`);
|
|
118
|
+
for (const d of report.diagnostics) {
|
|
119
|
+
const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : "stream";
|
|
120
|
+
const name = esc(`${d.rule} (${where})`);
|
|
121
|
+
lines.push(` <testcase name="${name}" classname="${suite}">`);
|
|
122
|
+
const body = `${esc(d.message)}\n${esc(d.specUrl)}`;
|
|
123
|
+
if (d.severity === "error") lines.push(` <failure message="${esc(d.message)}">${body}</failure>`);
|
|
124
|
+
else lines.push(` <skipped message="${esc(`${d.severity}: ${d.message}`)}">${body}</skipped>`);
|
|
125
|
+
lines.push(" </testcase>");
|
|
126
|
+
}
|
|
127
|
+
lines.push(" </testsuite>");
|
|
128
|
+
lines.push("</testsuites>");
|
|
129
|
+
return `${lines.join("\n")}\n`;
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
export { formatDiagnosticLine, formatReportSummary, toJUnit, toJsonReport, toSarif };
|
|
133
|
+
|
|
134
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","names":[],"sources":["../src/report/pretty.ts","../src/report/json.ts","../src/report/sarif.ts","../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 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\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":";;AAQA,MAAM,SAAmC;CAAE,OAAO;CAAK,SAAS;CAAK,MAAM;AAAI;AAC/E,MAAM,MAAgC;CAAE,OAAO;CAAM,SAAS;CAAM,MAAM;AAAK;AAE/E,SAAS,MAAM,MAAc,GAAW,IAAqB;CAC3D,OAAO,KAAK,QAAQ,KAAK,GAAG,EAAE,WAAW;AAC3C;AAEA,SAAgB,qBAAqB,GAAe,MAA6B;CAC/E,MAAM,QAAQ,EAAE,cAAc,IAAI,SAAS,EAAE,eAAe;CAC5D,MAAM,OAAO,MAAM,IAAI,EAAE,WAAW,GAAG,OAAO,EAAE,UAAU,GAAG,EAAE,QAAQ,KAAK,KAAK;CACjF,MAAM,OAAO,MAAM,KAAK,GAAG,EAAE,SAAS,OAAO,CAAC,EAAE,GAAG,MAAM,OAAO,EAAE,KAAK,KAAK,KAAK;CACjF,MAAM,OAAO,MAAM,KAAK,OAAO,EAAE,WAAW,KAAK,KAAK;CACtD,OAAO,GAAG,KAAK,IAAI,KAAK,GAAG,EAAE,QAAQ,IAAI;AAC3C;AAEA,SAAS,MAAM,GAAW,MAAsB;CAC9C,OAAO,GAAG,EAAE,GAAG,OAAO,MAAM,IAAI,KAAK;AACvC;AAEA,SAAgB,oBAAoB,QAAgB,MAA6B;CAC/E,MAAM,EAAE,QAAQ,UAAU,SAAS,OAAO;CAC1C,MAAM,QAAkB,CAAC;CAEzB,IAAI,SAAS,WAAW,SAAS,GAC/B,MAAM,KACJ,MAAM,MAAM,sCAAsC,MAAM,OAAO,YAAY,OAAO,KAAK,KAAK,KAAK,CACnG;MAEA,MAAM,KACJ,GAAG,MAAM,QAAQ,OAAO,EAAE,IAAI,MAAM,UAAU,SAAS,EAAE,IAAI,KAAK,eAAe,MAAM,OAAO,YAAY,OAAO,GACnH;CAGF,MAAM,WAAW,OAAO,QAAQ,OAAO,QAAQ;CAC/C,MAAM,YAAY,SAAS,QAAQ,GAAG,OAAO,MAAM,WAAW,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;CAC9E,MAAM,SAAS,UAAU,SAAS,IAAI,KAAK,UAAU,KAAK,IAAI,MAAM;CACpE,MAAM,KAAK,GAAG,UAAU,OAAO,MAAM,SAAS,OAAO,2BAA2B,QAAQ;CAExF,IAAI,OAAO,QAAQ,SAAS,GAAG;EAC7B,MAAM,KAAK,GAAG,MAAM,OAAO,QAAQ,QAAQ,MAAM,EAAE,gBAAgB;EACnE,KAAK,MAAM,KAAK,OAAO,SACrB,MAAM,KAAK,MAAM,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,UAAU,KAAK,KAAK,CAAC;CAEnE;CAEA,KAAK,MAAM,KAAK,OAAO,gBACrB,MAAM,KAAK,MAAM,MAAM,+BAA+B,KAAK,KAAK,KAAK,CAAC;CAGxE,OAAO,MAAM,KAAK,IAAI;AACxB;;;AC3CA,SAAgB,aAAa,QAAgB,MAA6C;CACxF,MAAM,MAA0B;EAAE,MAAM,KAAK;EAAM,GAAG;CAAO;CAC7D,IAAI,KAAK,WAAW,KAAA,GAAW,IAAI,SAAS,KAAK;CACjD,OAAO;AACT;;;AC6BA,MAAM,QAAoD;CACxD,OAAO;CACP,SAAS;CACT,MAAM;AACR;AASA,SAAgB,QAAQ,QAAgB,MAA8B;CACpE,MAAM,wBAAQ,IAAI,IAAuB;CACzC,KAAK,MAAM,KAAK,OAAO,aAAa;EAClC,IAAI,MAAM,IAAI,EAAE,IAAI,GAAG;EACvB,MAAM,QAAmB;GAAE,IAAI,EAAE;GAAM,SAAS,EAAE;EAAQ;EAC1D,MAAM,UAAU,MAAM,IAAI,EAAE,IAAI;EAChC,IAAI,YAAY,KAAA,GAAW;GACzB,MAAM,mBAAmB,EAAE,MAAM,QAAQ,MAAM;GAC/C,MAAM,uBAAuB,EAAE,OAAO,MAAM,QAAQ,UAAU;EAChE;EACA,MAAM,IAAI,EAAE,MAAM,KAAK;CACzB;CAEA,MAAM,UAAyB,OAAO,YAAY,KAAK,OAAO;EAC5D,QAAQ,EAAE;EACV,OAAO,MAAM,EAAE;EACf,SAAS,EAAE,MAAM,EAAE,QAAQ;EAC3B,WACE,KAAK,gBAAgB,KAAA,KAAa,EAAE,cAAc,IAC9C,CACE,EACE,kBAAkB;GAChB,kBAAkB,EAAE,KAAK,KAAK,YAAY;GAC1C,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE;EACxC,EACF,CACF,IACA,CAAC;CACT,EAAE;CAEF,OAAO;EACL,SAAS;EACT,SAAS;EACT,MAAM,CACJ;GACE,MAAM,EACJ,QAAQ;IACN,MAAM;IACN,SAAS,KAAK;IACd,gBAAgB;IAChB,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC;GAC3B,EACF;GACA;EACF,CACF;CACF;AACF;;;ACjGA,SAAS,IAAI,GAAmB;CAC9B,OAAO,EACJ,QAAQ,MAAM,OAAO,CAAC,CACtB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,QAAQ,CAAC,CACvB,QAAQ,MAAM,QAAQ;AAC3B;AAEA,SAAgB,QAAQ,QAAgB,MAA4B;CAClE,MAAM,QAAkB,CAAC,4CAAwC;CACjE,MAAM,QAAQ,IAAI,KAAK,IAAI;CAE3B,IAAI,OAAO,YAAY,WAAW,GAAG;EACnC,MAAM,KAAK,4FAAkF;EAC7F,MAAM,KAAK,sBAAsB,MAAM,iDAAiD;EACxF,MAAM,KACJ,+DAA+D,OAAO,WAAW,sBAAsB,MAAM,IAC/G;EACA,MAAM,KAAK,gBAAgB;EAC3B,MAAM,KAAK,eAAe;EAC1B,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;CAC7B;CAEA,MAAM,WAAW,OAAO,YAAY,QAAQ,MAAM,EAAE,aAAa,OAAO,CAAC,CAAC;CAC1E,MAAM,UAAU,OAAO,YAAY,SAAS;CAC5C,MAAM,SAAS,UAAU,OAAO,YAAY,OAAO,cAAc,SAAS,wBAAwB,QAAQ;CAC1G,MAAM,KAAK,qCAAqC,OAAO,EAAE;CACzD,MAAM,KAAK,sBAAsB,MAAM,IAAI,OAAO,EAAE;CACpD,KAAK,MAAM,KAAK,OAAO,aAAa;EAClC,MAAM,QAAQ,EAAE,cAAc,IAAI,SAAS,EAAE,eAAe;EAC5D,MAAM,OAAO,IAAI,GAAG,EAAE,KAAK,IAAI,MAAM,EAAE;EACvC,MAAM,KAAK,uBAAuB,KAAK,eAAe,MAAM,GAAG;EAC/D,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,EAAE,IAAI,IAAI,EAAE,OAAO;EAChD,IAAI,EAAE,aAAa,SACjB,MAAM,KAAK,2BAA2B,IAAI,EAAE,OAAO,EAAE,IAAI,KAAK,WAAW;OAEzE,MAAM,KAAK,2BAA2B,IAAI,GAAG,EAAE,SAAS,IAAI,EAAE,SAAS,EAAE,IAAI,KAAK,WAAW;EAE/F,MAAM,KAAK,iBAAiB;CAC9B;CACA,MAAM,KAAK,gBAAgB;CAC3B,MAAM,KAAK,eAAe;CAC1B,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;AAC7B"}
|