@cliwant/mcp-sam-gov 0.3.0 → 1.0.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 -21
- package/README.ja.md +48 -16
- package/README.ko.md +48 -16
- package/README.md +226 -64
- package/dist/attachments.d.ts +63 -0
- package/dist/attachments.d.ts.map +1 -0
- package/dist/attachments.js +576 -0
- package/dist/attachments.js.map +1 -0
- package/dist/bls.d.ts +267 -0
- package/dist/bls.d.ts.map +1 -0
- package/dist/bls.js +1549 -0
- package/dist/bls.js.map +1 -0
- package/dist/cache.d.ts +2 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +4 -0
- package/dist/cache.js.map +1 -1
- package/dist/census.d.ts +182 -0
- package/dist/census.d.ts.map +1 -0
- package/dist/census.js +576 -0
- package/dist/census.js.map +1 -0
- package/dist/ckan.d.ts +141 -0
- package/dist/ckan.d.ts.map +1 -0
- package/dist/ckan.js +393 -0
- package/dist/ckan.js.map +1 -0
- package/dist/clinicaltrials.d.ts +180 -0
- package/dist/clinicaltrials.d.ts.map +1 -0
- package/dist/clinicaltrials.js +730 -0
- package/dist/clinicaltrials.js.map +1 -0
- package/dist/cms.d.ts +140 -0
- package/dist/cms.d.ts.map +1 -0
- package/dist/cms.js +482 -0
- package/dist/cms.js.map +1 -0
- package/dist/coerce.d.ts +32 -0
- package/dist/coerce.d.ts.map +1 -0
- package/dist/coerce.js +50 -0
- package/dist/coerce.js.map +1 -0
- package/dist/datagov-catalog.d.ts +84 -0
- package/dist/datagov-catalog.d.ts.map +1 -0
- package/dist/datagov-catalog.js +233 -0
- package/dist/datagov-catalog.js.map +1 -0
- package/dist/datagov.d.ts +146 -0
- package/dist/datagov.d.ts.map +1 -0
- package/dist/datagov.js +689 -0
- package/dist/datagov.js.map +1 -0
- package/dist/datagovKey.d.ts +36 -0
- package/dist/datagovKey.d.ts.map +1 -0
- package/dist/datagovKey.js +60 -0
- package/dist/datagovKey.js.map +1 -0
- package/dist/datasource.d.ts +313 -0
- package/dist/datasource.d.ts.map +1 -0
- package/dist/datasource.js +551 -0
- package/dist/datasource.js.map +1 -0
- package/dist/disclosure.d.ts +55 -0
- package/dist/disclosure.d.ts.map +1 -0
- package/dist/disclosure.js +57 -0
- package/dist/disclosure.js.map +1 -0
- package/dist/ecfr.d.ts +4 -2
- package/dist/ecfr.d.ts.map +1 -1
- package/dist/ecfr.js +74 -1
- package/dist/ecfr.js.map +1 -1
- package/dist/echo.d.ts +143 -0
- package/dist/echo.d.ts.map +1 -0
- package/dist/echo.js +424 -0
- package/dist/echo.js.map +1 -0
- package/dist/edgar.d.ts +377 -0
- package/dist/edgar.d.ts.map +1 -0
- package/dist/edgar.js +2418 -0
- package/dist/edgar.js.map +1 -0
- package/dist/errors.d.ts +26 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +69 -1
- package/dist/errors.js.map +1 -1
- package/dist/fac.d.ts +180 -0
- package/dist/fac.d.ts.map +1 -0
- package/dist/fac.js +416 -0
- package/dist/fac.js.map +1 -0
- package/dist/far.d.ts +170 -0
- package/dist/far.d.ts.map +1 -0
- package/dist/far.js +804 -0
- package/dist/far.js.map +1 -0
- package/dist/fdic.d.ts +599 -0
- package/dist/fdic.d.ts.map +1 -0
- package/dist/fdic.js +1624 -0
- package/dist/fdic.js.map +1 -0
- package/dist/federal-register.d.ts +139 -2
- package/dist/federal-register.d.ts.map +1 -1
- package/dist/federal-register.js +419 -3
- package/dist/federal-register.js.map +1 -1
- package/dist/fema.d.ts +181 -0
- package/dist/fema.d.ts.map +1 -0
- package/dist/fema.js +436 -0
- package/dist/fema.js.map +1 -0
- package/dist/fpds.d.ts +108 -0
- package/dist/fpds.d.ts.map +1 -0
- package/dist/fpds.js +519 -0
- package/dist/fpds.js.map +1 -0
- package/dist/gao.d.ts +64 -0
- package/dist/gao.d.ts.map +1 -0
- package/dist/gao.js +640 -0
- package/dist/gao.js.map +1 -0
- package/dist/govinfo.d.ts +111 -0
- package/dist/govinfo.d.ts.map +1 -0
- package/dist/govinfo.js +422 -0
- package/dist/govinfo.js.map +1 -0
- package/dist/grants.d.ts +27 -4
- package/dist/grants.d.ts.map +1 -1
- package/dist/grants.js +111 -6
- package/dist/grants.js.map +1 -1
- package/dist/gsa-csv.d.ts +249 -0
- package/dist/gsa-csv.d.ts.map +1 -0
- package/dist/gsa-csv.js +784 -0
- package/dist/gsa-csv.js.map +1 -0
- package/dist/integrity.d.ts +212 -0
- package/dist/integrity.d.ts.map +1 -0
- package/dist/integrity.js +707 -0
- package/dist/integrity.js.map +1 -0
- package/dist/meta.d.ts +165 -0
- package/dist/meta.d.ts.map +1 -0
- package/dist/meta.js +162 -0
- package/dist/meta.js.map +1 -0
- package/dist/nih.d.ts +117 -0
- package/dist/nih.d.ts.map +1 -0
- package/dist/nih.js +291 -0
- package/dist/nih.js.map +1 -0
- package/dist/nppes.d.ts +157 -0
- package/dist/nppes.d.ts.map +1 -0
- package/dist/nppes.js +648 -0
- package/dist/nppes.js.map +1 -0
- package/dist/nsf.d.ts +176 -0
- package/dist/nsf.d.ts.map +1 -0
- package/dist/nsf.js +554 -0
- package/dist/nsf.js.map +1 -0
- package/dist/nvd.d.ts +176 -0
- package/dist/nvd.d.ts.map +1 -0
- package/dist/nvd.js +912 -0
- package/dist/nvd.js.map +1 -0
- package/dist/ofac.d.ts +205 -0
- package/dist/ofac.d.ts.map +1 -0
- package/dist/ofac.js +919 -0
- package/dist/ofac.js.map +1 -0
- package/dist/pricing.d.ts +110 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +843 -0
- package/dist/pricing.js.map +1 -0
- package/dist/sam-gov/client.d.ts +60 -2
- package/dist/sam-gov/client.d.ts.map +1 -1
- package/dist/sam-gov/client.js +320 -54
- package/dist/sam-gov/client.js.map +1 -1
- package/dist/sam-gov/index.d.ts +1 -1
- package/dist/sam-gov/index.d.ts.map +1 -1
- package/dist/sam-gov/index.js +1 -1
- package/dist/sam-gov/index.js.map +1 -1
- package/dist/sam-gov/types.d.ts +24 -0
- package/dist/sam-gov/types.d.ts.map +1 -1
- package/dist/sba.d.ts +72 -0
- package/dist/sba.d.ts.map +1 -0
- package/dist/sba.js +281 -0
- package/dist/sba.js.map +1 -0
- package/dist/server.d.ts +14 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +3889 -296
- package/dist/server.js.map +1 -1
- package/dist/snapshot.d.ts +98 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +146 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/socrata.d.ts +157 -0
- package/dist/socrata.d.ts.map +1 -0
- package/dist/socrata.js +448 -0
- package/dist/socrata.js.map +1 -0
- package/dist/treasury.d.ts +143 -0
- package/dist/treasury.d.ts.map +1 -0
- package/dist/treasury.js +436 -0
- package/dist/treasury.js.map +1 -0
- package/dist/usaspending.d.ts +260 -65
- package/dist/usaspending.d.ts.map +1 -1
- package/dist/usaspending.js +1629 -201
- package/dist/usaspending.js.map +1 -1
- package/dist/usitc.d.ts +142 -0
- package/dist/usitc.d.ts.map +1 -0
- package/dist/usitc.js +339 -0
- package/dist/usitc.js.map +1 -0
- package/package.json +24 -2
- package/src/attachments.ts +652 -0
- package/src/bls.ts +1943 -0
- package/src/cache.ts +5 -0
- package/src/census.ts +735 -0
- package/src/ckan.ts +495 -0
- package/src/clinicaltrials.ts +923 -0
- package/src/cms.ts +634 -0
- package/src/coerce.ts +47 -0
- package/src/datagov-catalog.ts +296 -0
- package/src/datagov.ts +907 -0
- package/src/datagovKey.ts +68 -0
- package/src/datasource.ts +721 -0
- package/src/disclosure.ts +61 -0
- package/src/ecfr.ts +98 -1
- package/src/echo.ts +496 -0
- package/src/edgar.ts +3014 -0
- package/src/errors.ts +84 -1
- package/src/fac.ts +529 -0
- package/src/far.ts +1007 -0
- package/src/fdic.ts +2052 -0
- package/src/federal-register.ts +512 -3
- package/src/fema.ts +541 -0
- package/src/fpds.ts +620 -0
- package/src/gao.ts +744 -0
- package/src/govinfo.ts +497 -0
- package/src/grants.ts +139 -7
- package/src/gsa-csv.ts +992 -0
- package/src/integrity.ts +928 -0
- package/src/meta.ts +292 -0
- package/src/nih.ts +375 -0
- package/src/nppes.ts +834 -0
- package/src/nsf.ts +706 -0
- package/src/nvd.ts +1124 -0
- package/src/ofac.ts +1166 -0
- package/src/pricing.ts +1075 -0
- package/src/sam-gov/client.ts +345 -63
- package/src/sam-gov/index.ts +5 -1
- package/src/sam-gov/types.ts +22 -0
- package/src/sba.ts +357 -0
- package/src/server.ts +4551 -328
- package/src/snapshot.ts +192 -0
- package/src/socrata.ts +532 -0
- package/src/treasury.ts +575 -0
- package/src/usaspending.ts +1946 -214
- package/src/usitc.ts +420 -0
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @cliwant/mcp-sam-gov/attachments — read the ACTUAL solicitation document.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists
|
|
5
|
+
* ----------------
|
|
6
|
+
* An AI doing procurement research needs the real RFP/SOW/Q&A/wage-table TEXT,
|
|
7
|
+
* not just the synopsis (`sam_fetch_description`) or metadata. A SAM notice
|
|
8
|
+
* carries ATTACHMENTS (`resourceLinks` → keyless download URLs) that hold the
|
|
9
|
+
* true requirements. The rest of the server exposes those URLs but cannot read
|
|
10
|
+
* them; this module fetches one keyless and returns its extracted text so the
|
|
11
|
+
* agent can analyze the buy.
|
|
12
|
+
*
|
|
13
|
+
* SAM attachments are predominantly PDF (compressed streams / CID fonts — not
|
|
14
|
+
* hand-rollable), so PDF text extraction uses `unpdf` (a single self-contained
|
|
15
|
+
* package that bundles pdfjs; no transitive deps). text/HTML also occur and are
|
|
16
|
+
* decoded/stripped locally. DOCX is a ZIP whose `word/document.xml` holds the
|
|
17
|
+
* body text (deflate-compressed); we parse the ZIP container by hand and inflate
|
|
18
|
+
* that entry with Node's built-in `node:zlib` — NO new dependency. Other
|
|
19
|
+
* binaries are NOT half-parsed — they return `text:null` with an honest note.
|
|
20
|
+
*
|
|
21
|
+
* Truthfulness invariants (a reviewer WILL attack these):
|
|
22
|
+
* - A DOWN fetch (5xx / network / timeout) THROWS `upstream_unavailable`
|
|
23
|
+
* (retryable). A down service is NOT an empty attachment — never `text:""`.
|
|
24
|
+
* - A 404 THROWS `not_found` (the attachment id is gone).
|
|
25
|
+
* - A PDF that fails to parse (encrypted/corrupt) → `text:null` +
|
|
26
|
+
* `extractionError` note — NEVER a crash, NEVER a fake "".
|
|
27
|
+
* - A DOCX that fails to parse (corrupt/not-a-zip/no `word/document.xml`) →
|
|
28
|
+
* `text:null` + a disclosed note — NEVER a crash, NEVER a fake "".
|
|
29
|
+
* - A non-extractable format (binary) → `text:null` + honest note.
|
|
30
|
+
* - SSRF: only sam.gov / api.sam.gov hosts are fetched; anything else →
|
|
31
|
+
* `invalid_input` with NO network call.
|
|
32
|
+
* - `truncated`/`pages` are honest.
|
|
33
|
+
*/
|
|
34
|
+
import zlib from "node:zlib";
|
|
35
|
+
import { extractText, getDocumentProxy } from "unpdf";
|
|
36
|
+
import { ToolErrorCarrier } from "./errors.js";
|
|
37
|
+
import { withMeta } from "./meta.js";
|
|
38
|
+
/** Default cap on returned text (chars). Large enough for a full RFP body. */
|
|
39
|
+
const DEFAULT_MAX_CHARS = 200_000;
|
|
40
|
+
/** Hard guard on the cap (mirrors the Zod max) — belt-and-suspenders. */
|
|
41
|
+
const MAX_MAX_CHARS = 500_000;
|
|
42
|
+
/**
|
|
43
|
+
* Refuse to buffer/parse an attachment larger than this (SAM/S3 send
|
|
44
|
+
* content-length; SAM files are well under this). Bounds memory before the whole
|
|
45
|
+
* body is read into an ArrayBuffer + parsed by unpdf.
|
|
46
|
+
*/
|
|
47
|
+
const MAX_ATTACHMENT_BYTES = 50 * 1024 * 1024;
|
|
48
|
+
/**
|
|
49
|
+
* Cap on the DECOMPRESSED size of a DOCX's `word/document.xml`. The fetch size
|
|
50
|
+
* guard bounds the compressed input, but deflate can expand ~1000× — a crafted
|
|
51
|
+
* "zip bomb" DOCX (SAM accepts vendor uploads) could inflate to GBs and OOM. A
|
|
52
|
+
* real Word body is far under this; `inflateRawSync` throws past it → caught →
|
|
53
|
+
* text:null + a disclosed note.
|
|
54
|
+
*/
|
|
55
|
+
const MAX_DOCX_XML_BYTES = 100 * 1024 * 1024;
|
|
56
|
+
/** Browser-ish UA — SAM serves the keyless file endpoint to a normal client. */
|
|
57
|
+
const BROWSER_UA = "Mozilla/5.0 (compatible; @cliwant/mcp-sam-gov; +https://github.com/cliwant/mcp-sam-gov)";
|
|
58
|
+
/** Fetch timeout for a (potentially large) attachment download. */
|
|
59
|
+
const FETCH_TIMEOUT_MS = 30_000;
|
|
60
|
+
/**
|
|
61
|
+
* SSRF allow-list: an `https://` URL whose host is exactly `sam.gov` or
|
|
62
|
+
* `api.sam.gov` (or a subdomain of `sam.gov`). We intentionally do NOT fetch
|
|
63
|
+
* arbitrary hosts — the input is a SAM attachment download URL, nothing else.
|
|
64
|
+
*/
|
|
65
|
+
function assertSamAttachmentUrl(url) {
|
|
66
|
+
let parsed;
|
|
67
|
+
try {
|
|
68
|
+
parsed = new URL(url);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
throw new ToolErrorCarrier({
|
|
72
|
+
kind: "invalid_input",
|
|
73
|
+
message: `Not a valid URL: ${JSON.stringify(url)}. Expected a SAM attachment download URL (https://sam.gov/... or https://api.sam.gov/...).`,
|
|
74
|
+
retryable: false,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (parsed.protocol !== "https:") {
|
|
78
|
+
throw new ToolErrorCarrier({
|
|
79
|
+
kind: "invalid_input",
|
|
80
|
+
message: `Only https:// SAM attachment URLs are fetched (got ${parsed.protocol}//). SSRF hygiene: this tool fetches sam.gov / api.sam.gov only.`,
|
|
81
|
+
retryable: false,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const host = parsed.hostname.toLowerCase();
|
|
85
|
+
const isSam = host === "sam.gov" ||
|
|
86
|
+
host === "api.sam.gov" ||
|
|
87
|
+
host.endsWith(".sam.gov");
|
|
88
|
+
if (!isSam) {
|
|
89
|
+
throw new ToolErrorCarrier({
|
|
90
|
+
kind: "invalid_input",
|
|
91
|
+
message: `Refusing to fetch non-SAM host ${JSON.stringify(host)}. This tool only reads sam.gov / api.sam.gov attachment URLs (SSRF hygiene + scope). Pass a URL from sam_get_opportunity's attachments[].url.`,
|
|
92
|
+
retryable: false,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return parsed;
|
|
96
|
+
}
|
|
97
|
+
/** Parse a filename out of a `content-disposition` header, if present. */
|
|
98
|
+
function filenameFromDisposition(disposition) {
|
|
99
|
+
if (!disposition)
|
|
100
|
+
return null;
|
|
101
|
+
// RFC 5987 `filename*=UTF-8''...` takes precedence when present.
|
|
102
|
+
const star = /filename\*=(?:UTF-8'')?([^;]+)/i.exec(disposition);
|
|
103
|
+
if (star && star[1]) {
|
|
104
|
+
try {
|
|
105
|
+
return decodeURIComponent(star[1].trim().replace(/^"|"$/g, ""));
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
/* fall through to plain filename */
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const plain = /filename="?([^";]+)"?/i.exec(disposition);
|
|
112
|
+
if (plain && plain[1])
|
|
113
|
+
return plain[1].trim();
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
/** Lowercased file extension (no dot) from a filename, or "". */
|
|
117
|
+
function extOf(filename) {
|
|
118
|
+
if (!filename)
|
|
119
|
+
return "";
|
|
120
|
+
const m = /\.([A-Za-z0-9]+)\s*$/.exec(filename);
|
|
121
|
+
return m && m[1] ? m[1].toLowerCase() : "";
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Detect the format from magic bytes (authoritative) + filename ext +
|
|
125
|
+
* content-type (advisory). Magic bytes win because SAM serves everything as
|
|
126
|
+
* `application/octet-stream`.
|
|
127
|
+
*/
|
|
128
|
+
function detectFormat(bytes, contentType, ext) {
|
|
129
|
+
const ct = contentType.toLowerCase();
|
|
130
|
+
// %PDF
|
|
131
|
+
if (bytes.length >= 4 &&
|
|
132
|
+
bytes[0] === 0x25 &&
|
|
133
|
+
bytes[1] === 0x50 &&
|
|
134
|
+
bytes[2] === 0x44 &&
|
|
135
|
+
bytes[3] === 0x46) {
|
|
136
|
+
return "pdf";
|
|
137
|
+
}
|
|
138
|
+
// PK\x03\x04 (ZIP container) → treat as docx ONLY when the ext/CT say so;
|
|
139
|
+
// other zip-based Office/archive formats fall through to "binary".
|
|
140
|
+
if (bytes.length >= 4 &&
|
|
141
|
+
bytes[0] === 0x50 &&
|
|
142
|
+
bytes[1] === 0x4b &&
|
|
143
|
+
bytes[2] === 0x03 &&
|
|
144
|
+
bytes[3] === 0x04) {
|
|
145
|
+
if (ext === "docx" || ct.includes("wordprocessingml"))
|
|
146
|
+
return "docx";
|
|
147
|
+
return "binary";
|
|
148
|
+
}
|
|
149
|
+
// HTML: content-type says so, OR the body starts (after BOM/whitespace) with
|
|
150
|
+
// '<' plus an html-ish token.
|
|
151
|
+
if (ct.includes("text/html") || ct.includes("application/xhtml")) {
|
|
152
|
+
return "html";
|
|
153
|
+
}
|
|
154
|
+
const head = sniffLeadingText(bytes, 512).trimStart().toLowerCase();
|
|
155
|
+
if (head.startsWith("<!doctype html") || head.startsWith("<html")) {
|
|
156
|
+
return "html";
|
|
157
|
+
}
|
|
158
|
+
// A body that opens with a tag and carries an html-ish element (but is NOT an
|
|
159
|
+
// XML declaration) reads as HTML.
|
|
160
|
+
if (!head.startsWith("<?xml") &&
|
|
161
|
+
head.startsWith("<") &&
|
|
162
|
+
/<(html|body|div|p|table|head)\b/.test(head)) {
|
|
163
|
+
return "html";
|
|
164
|
+
}
|
|
165
|
+
// Plain textual content-types.
|
|
166
|
+
if (ct.includes("text/plain") ||
|
|
167
|
+
ct.includes("text/csv") ||
|
|
168
|
+
ct.includes("application/csv") ||
|
|
169
|
+
ct.includes("text/xml") ||
|
|
170
|
+
ct.includes("application/xml") ||
|
|
171
|
+
ct.includes("application/json") ||
|
|
172
|
+
ct.includes("text/")) {
|
|
173
|
+
return "text";
|
|
174
|
+
}
|
|
175
|
+
// Extension-based textual fallback (SAM serves octet-stream).
|
|
176
|
+
if (["txt", "csv", "xml", "json", "tsv", "md", "log"].includes(ext)) {
|
|
177
|
+
return "text";
|
|
178
|
+
}
|
|
179
|
+
// Looks like text if it decodes cleanly with no NUL/control soup.
|
|
180
|
+
if (looksTextual(bytes))
|
|
181
|
+
return "text";
|
|
182
|
+
return "binary";
|
|
183
|
+
}
|
|
184
|
+
/** Decode the first `n` bytes as UTF-8 for a cheap content sniff. */
|
|
185
|
+
function sniffLeadingText(bytes, n) {
|
|
186
|
+
const slice = bytes.subarray(0, Math.min(n, bytes.length));
|
|
187
|
+
try {
|
|
188
|
+
return new TextDecoder("utf-8", { fatal: false }).decode(slice);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return "";
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Heuristic: does the byte buffer look like human-readable text? True when
|
|
196
|
+
* there are no NUL bytes and the share of control chars (outside tab/CR/LF) is
|
|
197
|
+
* tiny. Used only as a last-resort fallback when magic bytes + CT + ext are
|
|
198
|
+
* all inconclusive.
|
|
199
|
+
*/
|
|
200
|
+
function looksTextual(bytes) {
|
|
201
|
+
const sample = bytes.subarray(0, Math.min(1024, bytes.length));
|
|
202
|
+
if (sample.length === 0)
|
|
203
|
+
return false;
|
|
204
|
+
let control = 0;
|
|
205
|
+
for (const b of sample) {
|
|
206
|
+
if (b === 0)
|
|
207
|
+
return false; // NUL ⇒ binary
|
|
208
|
+
if (b < 0x09 || (b > 0x0d && b < 0x20))
|
|
209
|
+
control++;
|
|
210
|
+
}
|
|
211
|
+
return control / sample.length < 0.05;
|
|
212
|
+
}
|
|
213
|
+
/** Decode common HTML/XML entities that survive tag-stripping. */
|
|
214
|
+
function decodeEntities(s) {
|
|
215
|
+
return s
|
|
216
|
+
.replace(/ /g, " ")
|
|
217
|
+
.replace(/</g, "<")
|
|
218
|
+
.replace(/>/g, ">")
|
|
219
|
+
.replace(/"/g, '"')
|
|
220
|
+
.replace(/�?39;/g, "'")
|
|
221
|
+
.replace(/'/g, "'")
|
|
222
|
+
.replace(/'/g, "'")
|
|
223
|
+
// & last so we don't double-decode an already-decoded entity.
|
|
224
|
+
.replace(/&/g, "&");
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Strip HTML to readable text: drop <script>/<style> CONTENT entirely, remove
|
|
228
|
+
* all remaining tags, decode entities, collapse whitespace. Mirrors the
|
|
229
|
+
* approach used elsewhere in the server (sam-gov description, gao, ecfr).
|
|
230
|
+
*/
|
|
231
|
+
function stripHtml(html) {
|
|
232
|
+
return decodeEntities(html
|
|
233
|
+
.replace(/<style[\s\S]*?<\/style>/gi, " ")
|
|
234
|
+
.replace(/<script[\s\S]*?<\/script>/gi, " ")
|
|
235
|
+
.replace(/<[^>]+>/g, " "))
|
|
236
|
+
.replace(/\s+/g, " ")
|
|
237
|
+
.trim();
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Extract the body text of a DOCX (Office Open XML) file, dependency-free.
|
|
241
|
+
*
|
|
242
|
+
* A DOCX is a ZIP archive; the document body lives in `word/document.xml`
|
|
243
|
+
* (normally deflate-compressed, ZIP method 8). We parse the ZIP container by
|
|
244
|
+
* hand — locate the End-Of-Central-Directory record, walk the central
|
|
245
|
+
* directory to find `word/document.xml`, read its local file header to compute
|
|
246
|
+
* the compressed-data offset, slice those bytes, and inflate them with Node's
|
|
247
|
+
* built-in `node:zlib` (`inflateRawSync`; method 0 = stored → bytes as-is). The
|
|
248
|
+
* resulting XML is reduced to plain text: `</w:p>`/`</w:tr>` become newlines,
|
|
249
|
+
* `<w:tab/>` a tab, ALL remaining tags are stripped whole (so open-tag
|
|
250
|
+
* attributes like `w14:paraId="…"` never leak), entities are decoded, and
|
|
251
|
+
* runs of intra-line whitespace are collapsed.
|
|
252
|
+
*
|
|
253
|
+
* DEFENSIVE: every offset/length read is bounds-checked. Any structural problem
|
|
254
|
+
* (buffer too small, EOCD/CD/LFH signature bad, an offset or length past the
|
|
255
|
+
* end of the buffer, `word/document.xml` absent, an unsupported compression
|
|
256
|
+
* method, or an inflate throw) raises a normal `Error`. It never reads out of
|
|
257
|
+
* bounds, never loops unbounded, and never returns garbage — the CALLER catches
|
|
258
|
+
* the throw and reports `text:null` + a disclosed note.
|
|
259
|
+
*
|
|
260
|
+
* @throws {Error} on any malformed/unsupported input.
|
|
261
|
+
*/
|
|
262
|
+
function extractDocxText(bytes) {
|
|
263
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
264
|
+
const u32 = (o) => dv.getUint32(o, true); // little-endian
|
|
265
|
+
const u16 = (o) => dv.getUint16(o, true);
|
|
266
|
+
const utf8 = (start, end) => new TextDecoder("utf-8", { fatal: false }).decode(bytes.subarray(start, end));
|
|
267
|
+
const EOCD_SIG = 0x06054b50; // PK\x05\x06 End-Of-Central-Directory
|
|
268
|
+
const CD_SIG = 0x02014b50; // PK\x01\x02 central-directory file header
|
|
269
|
+
const LFH_SIG = 0x04034b50; // PK\x03\x04 local file header
|
|
270
|
+
const n = bytes.length;
|
|
271
|
+
// EOCD is 22 bytes minimum (before any trailing comment).
|
|
272
|
+
if (n < 22)
|
|
273
|
+
throw new Error("not a valid DOCX (file too small to be a ZIP)");
|
|
274
|
+
// 1. Locate the End-Of-Central-Directory by scanning backward. It sits within
|
|
275
|
+
// the last 22 bytes + an optional comment (<= 0xffff), so bound the scan.
|
|
276
|
+
let eocd = -1;
|
|
277
|
+
const minStart = Math.max(0, n - 22 - 0xffff);
|
|
278
|
+
for (let i = n - 22; i >= minStart; i--) {
|
|
279
|
+
if (u32(i) === EOCD_SIG) {
|
|
280
|
+
eocd = i;
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (eocd < 0) {
|
|
285
|
+
throw new Error("not a valid DOCX (ZIP End-Of-Central-Directory not found)");
|
|
286
|
+
}
|
|
287
|
+
const entryCount = u16(eocd + 10);
|
|
288
|
+
const cdOffset = u32(eocd + 16);
|
|
289
|
+
if (cdOffset >= n) {
|
|
290
|
+
throw new Error("corrupt DOCX (ZIP central-directory offset past end of file)");
|
|
291
|
+
}
|
|
292
|
+
// 2. Walk the central directory to find `word/document.xml`.
|
|
293
|
+
let p = cdOffset;
|
|
294
|
+
let target = null;
|
|
295
|
+
for (let e = 0; e < entryCount; e++) {
|
|
296
|
+
if (p + 46 > n) {
|
|
297
|
+
throw new Error("corrupt DOCX (ZIP central-directory entry truncated)");
|
|
298
|
+
}
|
|
299
|
+
if (u32(p) !== CD_SIG) {
|
|
300
|
+
throw new Error("corrupt DOCX (ZIP central-directory signature invalid)");
|
|
301
|
+
}
|
|
302
|
+
const method = u16(p + 10);
|
|
303
|
+
const compressedSize = u32(p + 20);
|
|
304
|
+
const nameLen = u16(p + 28);
|
|
305
|
+
const extraLen = u16(p + 30);
|
|
306
|
+
const commentLen = u16(p + 32);
|
|
307
|
+
const lho = u32(p + 42);
|
|
308
|
+
const nameStart = p + 46;
|
|
309
|
+
if (nameStart + nameLen > n) {
|
|
310
|
+
throw new Error("corrupt DOCX (ZIP entry name past end of file)");
|
|
311
|
+
}
|
|
312
|
+
const name = utf8(nameStart, nameStart + nameLen);
|
|
313
|
+
if (name === "word/document.xml") {
|
|
314
|
+
target = { method, compressedSize, lho };
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
p = nameStart + nameLen + extraLen + commentLen;
|
|
318
|
+
}
|
|
319
|
+
if (!target) {
|
|
320
|
+
throw new Error("not a valid Word document (word/document.xml not found in the ZIP)");
|
|
321
|
+
}
|
|
322
|
+
// 3. Read the local file header to compute where the entry's data begins.
|
|
323
|
+
const { method, compressedSize, lho } = target;
|
|
324
|
+
if (lho + 30 > n) {
|
|
325
|
+
throw new Error("corrupt DOCX (ZIP local file header past end of file)");
|
|
326
|
+
}
|
|
327
|
+
if (u32(lho) !== LFH_SIG) {
|
|
328
|
+
throw new Error("corrupt DOCX (ZIP local file header signature invalid)");
|
|
329
|
+
}
|
|
330
|
+
const localNameLen = u16(lho + 26);
|
|
331
|
+
const localExtraLen = u16(lho + 28);
|
|
332
|
+
const dataStart = lho + 30 + localNameLen + localExtraLen;
|
|
333
|
+
if (dataStart + compressedSize > n) {
|
|
334
|
+
throw new Error("corrupt DOCX (ZIP entry data past end of file)");
|
|
335
|
+
}
|
|
336
|
+
const comp = bytes.subarray(dataStart, dataStart + compressedSize);
|
|
337
|
+
// 4. Decompress: method 8 = deflate (raw), method 0 = stored (as-is).
|
|
338
|
+
let xmlBytes;
|
|
339
|
+
if (method === 8) {
|
|
340
|
+
// maxOutputLength caps the inflated size → throws on a zip-bomb DOCX.
|
|
341
|
+
xmlBytes = zlib.inflateRawSync(comp, { maxOutputLength: MAX_DOCX_XML_BYTES });
|
|
342
|
+
}
|
|
343
|
+
else if (method === 0) {
|
|
344
|
+
xmlBytes = comp;
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
throw new Error(`unsupported DOCX ZIP compression method ${method}`);
|
|
348
|
+
}
|
|
349
|
+
const xml = new TextDecoder("utf-8", { fatal: false }).decode(xmlBytes);
|
|
350
|
+
// 5. XML → text. Convert paragraph/row breaks and tabs to whitespace FIRST,
|
|
351
|
+
// then strip ALL remaining tags whole (`<w:p w14:paraId="…">` included, so
|
|
352
|
+
// no attribute text leaks), decode entities, collapse intra-line runs.
|
|
353
|
+
const text = decodeEntities(xml
|
|
354
|
+
.replace(/<\/w:p>/g, "\n")
|
|
355
|
+
.replace(/<\/w:tr>/g, "\n")
|
|
356
|
+
.replace(/<w:tab\b[^>]*\/?>/g, "\t")
|
|
357
|
+
.replace(/<[^>]+>/g, ""))
|
|
358
|
+
.replace(/[ \t]+/g, " ")
|
|
359
|
+
.replace(/ *\n */g, "\n")
|
|
360
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
361
|
+
.trim();
|
|
362
|
+
return text;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Fetch a SAM attachment keyless and return its extracted TEXT.
|
|
366
|
+
*
|
|
367
|
+
* See the module header for the truthfulness invariants. Throws a
|
|
368
|
+
* `ToolErrorCarrier` for: bad/non-SAM URL (`invalid_input`, no fetch), a 404
|
|
369
|
+
* (`not_found`), or a down fetch (`upstream_unavailable`, retryable). Otherwise
|
|
370
|
+
* ALWAYS returns a payload — a document we cannot extract yields `text:null` +
|
|
371
|
+
* a disclosed note, never an exception and never a fabricated string.
|
|
372
|
+
*/
|
|
373
|
+
export async function fetchAttachmentText(args) {
|
|
374
|
+
const parsed = assertSamAttachmentUrl(args.url);
|
|
375
|
+
const url = parsed.toString();
|
|
376
|
+
const maxChars = Math.min(Math.max(1, args.maxChars ?? DEFAULT_MAX_CHARS), MAX_MAX_CHARS);
|
|
377
|
+
// ── Fetch keyless. A down fetch is an OUTAGE (throw), never empty text. ──
|
|
378
|
+
let res;
|
|
379
|
+
try {
|
|
380
|
+
res = await fetch(url, {
|
|
381
|
+
headers: { "User-Agent": BROWSER_UA, Accept: "*/*" },
|
|
382
|
+
redirect: "follow",
|
|
383
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
catch (e) {
|
|
387
|
+
// Network-level fault or timeout ⇒ retryable outage.
|
|
388
|
+
const err = {
|
|
389
|
+
kind: "upstream_unavailable",
|
|
390
|
+
message: `Network error fetching SAM attachment ${url}: ${e.message}. The service is unavailable, NOT an empty attachment — retry.`,
|
|
391
|
+
retryable: true,
|
|
392
|
+
retryAfterSeconds: 30,
|
|
393
|
+
upstreamEndpoint: "sam:attachment",
|
|
394
|
+
};
|
|
395
|
+
throw new ToolErrorCarrier(err);
|
|
396
|
+
}
|
|
397
|
+
if (res.status === 404) {
|
|
398
|
+
throw new ToolErrorCarrier({
|
|
399
|
+
kind: "not_found",
|
|
400
|
+
message: `SAM attachment not found (HTTP 404) at ${url}. The attachment id is gone.`,
|
|
401
|
+
retryable: false,
|
|
402
|
+
upstreamStatus: 404,
|
|
403
|
+
upstreamEndpoint: "sam:attachment",
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
if (!res.ok) {
|
|
407
|
+
// Any other non-2xx (5xx, 429, transient 4xx) ⇒ treat as a retryable
|
|
408
|
+
// outage. A DOWN attachment endpoint is NOT an empty document.
|
|
409
|
+
throw new ToolErrorCarrier({
|
|
410
|
+
kind: "upstream_unavailable",
|
|
411
|
+
message: `SAM attachment fetch returned HTTP ${res.status} at ${url} — the service is unavailable, NOT an empty attachment. Retry.`,
|
|
412
|
+
retryable: true,
|
|
413
|
+
retryAfterSeconds: 60,
|
|
414
|
+
upstreamStatus: res.status,
|
|
415
|
+
upstreamEndpoint: "sam:attachment",
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
// Redirect safety (SSRF defense-in-depth): `fetch` followed redirects — SAM's
|
|
419
|
+
// download endpoint 303-redirects to a time-signed S3 URL and the extraction
|
|
420
|
+
// DEPENDS on that hop, so off-host redirects can't be forbidden outright.
|
|
421
|
+
// Instead re-validate the FINAL host: allow *.sam.gov and SAM's S3 attachment
|
|
422
|
+
// store, reject anything else — a redirect must never reach an internal /
|
|
423
|
+
// cloud-metadata / attacker host and have its body read back as "text".
|
|
424
|
+
const finalHost = (() => {
|
|
425
|
+
try {
|
|
426
|
+
return new URL(res.url).hostname.toLowerCase();
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
return "";
|
|
430
|
+
}
|
|
431
|
+
})();
|
|
432
|
+
// SAM's attachment endpoint 303-redirects to a SINGLE time-signed S3 bucket
|
|
433
|
+
// (LIVE-VERIFIED 2026-07-05: `iae-fbo-attachments.s3.amazonaws.com`). Pin THAT
|
|
434
|
+
// bucket — across S3 host-form/region variants (`<b>.s3.amazonaws.com`,
|
|
435
|
+
// `<b>.s3.<region>.amazonaws.com`, `<b>.s3-<region>.amazonaws.com`, dualstack)
|
|
436
|
+
// — rather than trusting ALL of AWS S3: a sam.gov→attacker-owned-bucket redirect
|
|
437
|
+
// (any `*.s3.amazonaws.com` is registrable) must NOT have its body read back as
|
|
438
|
+
// "SAM text". Fail-closed: an unknown bucket → rejected with a clear error.
|
|
439
|
+
const SAM_S3_BUCKETS = ["iae-fbo-attachments"];
|
|
440
|
+
const isSamS3Host = (h) => h.endsWith(".amazonaws.com") &&
|
|
441
|
+
SAM_S3_BUCKETS.some((b) => h.startsWith(`${b}.s3.`) || h.startsWith(`${b}.s3-`));
|
|
442
|
+
const finalHostOk =
|
|
443
|
+
// Only trust an EMPTY final host when NO redirect occurred; if `fetch` redirected
|
|
444
|
+
// but the runtime hid the URL we cannot verify the target → reject (was trusted).
|
|
445
|
+
(finalHost === "" && !res.redirected) ||
|
|
446
|
+
finalHost === "sam.gov" ||
|
|
447
|
+
finalHost.endsWith(".sam.gov") ||
|
|
448
|
+
isSamS3Host(finalHost);
|
|
449
|
+
if (!finalHostOk) {
|
|
450
|
+
throw new ToolErrorCarrier({
|
|
451
|
+
kind: "invalid_input",
|
|
452
|
+
message: `The SAM attachment URL redirected to an unexpected host ${JSON.stringify(finalHost)}; refusing to read it back (SSRF safety). Expected sam.gov or SAM's S3 attachment store.`,
|
|
453
|
+
retryable: false,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
// Size guard: refuse an attachment larger than the cap BEFORE buffering it
|
|
457
|
+
// whole (unpdf then parses the whole buffer). SAM/S3 send content-length.
|
|
458
|
+
const declaredLen = Number(res.headers.get("content-length") ?? "");
|
|
459
|
+
if (Number.isFinite(declaredLen) && declaredLen > MAX_ATTACHMENT_BYTES) {
|
|
460
|
+
throw new ToolErrorCarrier({
|
|
461
|
+
kind: "invalid_input",
|
|
462
|
+
message: `SAM attachment is ${Math.round(declaredLen / 1048576)} MB, over this tool's ${Math.round(MAX_ATTACHMENT_BYTES / 1048576)} MB limit — download it directly at ${url}.`,
|
|
463
|
+
retryable: false,
|
|
464
|
+
upstreamEndpoint: "sam:attachment",
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
const contentType = res.headers.get("content-type");
|
|
468
|
+
const disposition = res.headers.get("content-disposition");
|
|
469
|
+
const filename = filenameFromDisposition(disposition);
|
|
470
|
+
const ext = extOf(filename);
|
|
471
|
+
const buf = await res.arrayBuffer();
|
|
472
|
+
const bytes = new Uint8Array(buf);
|
|
473
|
+
const sizeBytes = bytes.byteLength;
|
|
474
|
+
const format = detectFormat(bytes, contentType ?? "", ext);
|
|
475
|
+
const notes = [];
|
|
476
|
+
let text = null;
|
|
477
|
+
let pages = null;
|
|
478
|
+
if (format === "pdf") {
|
|
479
|
+
try {
|
|
480
|
+
const pdf = await getDocumentProxy(bytes);
|
|
481
|
+
const { totalPages, text: extracted } = await extractText(pdf, {
|
|
482
|
+
mergePages: true,
|
|
483
|
+
});
|
|
484
|
+
pages = totalPages;
|
|
485
|
+
text = extracted;
|
|
486
|
+
}
|
|
487
|
+
catch (e) {
|
|
488
|
+
// Encrypted / corrupt / unsupported PDF. This is NOT an outage and NOT an
|
|
489
|
+
// empty document — disclose the failure and return text:null.
|
|
490
|
+
text = null;
|
|
491
|
+
pages = null;
|
|
492
|
+
notes.push(`PDF text extraction failed (extractionError): ${e.message}. The file may be encrypted, corrupt, or image-only (scanned). It was NOT read; download it at ${url}.`);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
else if (format === "docx") {
|
|
496
|
+
// DOCX (ZIP + word/document.xml): extract the body text dependency-free.
|
|
497
|
+
// A corrupt/invalid/no-word-document DOCX is NOT an outage and NOT an empty
|
|
498
|
+
// document — disclose the failure and return text:null (the B1 empty-guard
|
|
499
|
+
// downstream turns a genuinely empty body into text:null + a disclosed note).
|
|
500
|
+
try {
|
|
501
|
+
text = extractDocxText(bytes);
|
|
502
|
+
pages = null;
|
|
503
|
+
}
|
|
504
|
+
catch (e) {
|
|
505
|
+
text = null;
|
|
506
|
+
pages = null;
|
|
507
|
+
notes.push(`DOCX text extraction failed: ${e.message}. The file may be corrupt or not a valid Word document — download it at ${url}.`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
else if (format === "html") {
|
|
511
|
+
text = stripHtml(new TextDecoder("utf-8", { fatal: false }).decode(bytes));
|
|
512
|
+
}
|
|
513
|
+
else if (format === "text") {
|
|
514
|
+
text = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
// binary / other — do NOT half-parse.
|
|
518
|
+
text = null;
|
|
519
|
+
pages = null;
|
|
520
|
+
notes.push(`${format} content is not extractable keyless in this tool — download it at ${url}.`);
|
|
521
|
+
}
|
|
522
|
+
// ── Validate the extracted text is REAL content, not a masquerade. ──
|
|
523
|
+
// A fetch+parse that yields EMPTY text (an image-only/scanned PDF with no text
|
|
524
|
+
// layer — common for wage determinations, signed SF-1442s, drawings — or an
|
|
525
|
+
// empty body) or DECODE-GARBAGE (a non-UTF-8 charset mis-decoded into NUL /
|
|
526
|
+
// replacement-char soup) must NOT read as "the document's text is empty/this".
|
|
527
|
+
// Report text:null + a disclosed reason instead (a scanned PDF keeps its honest
|
|
528
|
+
// `pages` count — it HAS pages, just no text layer).
|
|
529
|
+
if (text != null) {
|
|
530
|
+
const replacementShare = (text.match(/\uFFFD/g)?.length ?? 0) / Math.max(1, text.length);
|
|
531
|
+
if (text.trim().length === 0) {
|
|
532
|
+
notes.push(format === "pdf"
|
|
533
|
+
? `This PDF has NO extractable text layer — it is likely scanned/image-only (a photo of the page). It was NOT read as text; download it at ${url} (or run OCR).`
|
|
534
|
+
: `The ${format} attachment decoded to empty/whitespace content — nothing to read; download it at ${url}.`);
|
|
535
|
+
text = null;
|
|
536
|
+
}
|
|
537
|
+
else if (text.includes("\u0000") || replacementShare > 0.1) {
|
|
538
|
+
notes.push(`The ${format} attachment could not be decoded as readable UTF-8 text (it likely uses a different character set, or is binary mislabeled as text) — download it at ${url}.`);
|
|
539
|
+
text = null;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
// ── Truncate honestly. ──
|
|
543
|
+
let truncated = false;
|
|
544
|
+
if (text != null && text.length > maxChars) {
|
|
545
|
+
text = text.slice(0, maxChars);
|
|
546
|
+
truncated = true;
|
|
547
|
+
notes.push(`Text truncated to maxChars=${maxChars} characters (the document is longer). Raise maxChars or fetch the file directly for the full text.`);
|
|
548
|
+
}
|
|
549
|
+
const extracted = text != null;
|
|
550
|
+
if (extracted) {
|
|
551
|
+
notes.push(`Detected format: ${format}.`);
|
|
552
|
+
}
|
|
553
|
+
const data = {
|
|
554
|
+
url,
|
|
555
|
+
filename,
|
|
556
|
+
contentType,
|
|
557
|
+
format,
|
|
558
|
+
sizeBytes,
|
|
559
|
+
pages,
|
|
560
|
+
text,
|
|
561
|
+
truncated,
|
|
562
|
+
extracted,
|
|
563
|
+
};
|
|
564
|
+
return withMeta(data, {
|
|
565
|
+
source: "sam.gov (keyless attachment)",
|
|
566
|
+
keylessMode: true,
|
|
567
|
+
// We fully delivered the document iff we extracted text AND did not truncate.
|
|
568
|
+
complete: extracted && !truncated ? undefined : false,
|
|
569
|
+
truncated,
|
|
570
|
+
returned: extracted ? 1 : 0,
|
|
571
|
+
totalAvailable: null,
|
|
572
|
+
fieldsUnavailable: extracted ? [] : ["text"],
|
|
573
|
+
notes,
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
//# sourceMappingURL=attachments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachments.js","sourceRoot":"","sources":["../src/attachments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAkB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAmB,MAAM,WAAW,CAAC;AAEtD,8EAA8E;AAC9E,MAAM,iBAAiB,GAAG,OAAO,CAAC;AAClC,yEAAyE;AACzE,MAAM,aAAa,GAAG,OAAO,CAAC;AAC9B;;;;GAIG;AACH,MAAM,oBAAoB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAC9C;;;;;;GAMG;AACH,MAAM,kBAAkB,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AAE7C,gFAAgF;AAChF,MAAM,UAAU,GACd,yFAAyF,CAAC;AAE5F,mEAAmE;AACnE,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEhC;;;;GAIG;AACH,SAAS,sBAAsB,CAAC,GAAW;IACzC,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,gBAAgB,CAAC;YACzB,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,oBAAoB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,4FAA4F;YAC5I,SAAS,EAAE,KAAK;SACjB,CAAC,CAAC;IACL,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,gBAAgB,CAAC;YACzB,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,sDAAsD,MAAM,CAAC,QAAQ,kEAAkE;YAChJ,SAAS,EAAE,KAAK;SACjB,CAAC,CAAC;IACL,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,KAAK,GACT,IAAI,KAAK,SAAS;QAClB,IAAI,KAAK,aAAa;QACtB,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC5B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,gBAAgB,CAAC;YACzB,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,kCAAkC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,+IAA+I;YAC9M,SAAS,EAAE,KAAK;SACjB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,0EAA0E;AAC1E,SAAS,uBAAuB,CAAC,WAA0B;IACzD,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAC9B,iEAAiE;IACjE,MAAM,IAAI,GAAG,iCAAiC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACjE,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;QAClE,CAAC;QAAC,MAAM,CAAC;YACP,oCAAoC;QACtC,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACzD,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9C,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iEAAiE;AACjE,SAAS,KAAK,CAAC,QAAuB;IACpC,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzB,MAAM,CAAC,GAAG,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC7C,CAAC;AAKD;;;;GAIG;AACH,SAAS,YAAY,CACnB,KAAiB,EACjB,WAAmB,EACnB,GAAW;IAEX,MAAM,EAAE,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC;IACrC,OAAO;IACP,IACE,KAAK,CAAC,MAAM,IAAI,CAAC;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,EACjB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,0EAA0E;IAC1E,mEAAmE;IACnE,IACE,KAAK,CAAC,MAAM,IAAI,CAAC;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,EACjB,CAAC;QACD,IAAI,GAAG,KAAK,MAAM,IAAI,EAAE,CAAC,QAAQ,CAAC,kBAAkB,CAAC;YAAE,OAAO,MAAM,CAAC;QACrE,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,6EAA6E;IAC7E,8BAA8B;IAC9B,IAAI,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACjE,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,WAAW,EAAE,CAAC;IACpE,IAAI,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAClE,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,8EAA8E;IAC9E,kCAAkC;IAClC,IACE,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QACzB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QACpB,iCAAiC,CAAC,IAAI,CAAC,IAAI,CAAC,EAC5C,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,+BAA+B;IAC/B,IACE,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC;QACzB,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC;QACvB,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QAC9B,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC;QACvB,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QAC9B,EAAE,CAAC,QAAQ,CAAC,kBAAkB,CAAC;QAC/B,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,EACpB,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,8DAA8D;IAC9D,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACpE,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,kEAAkE;IAClE,IAAI,YAAY,CAAC,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IACvC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,qEAAqE;AACrE,SAAS,gBAAgB,CAAC,KAAiB,EAAE,CAAS;IACpD,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3D,IAAI,CAAC;QACH,OAAO,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,KAAiB;IACrC,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC,CAAC,eAAe;QAC1C,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;IACpD,CAAC;IACD,OAAO,OAAO,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC;AACxC,CAAC;AAED,kEAAkE;AAClE,SAAS,cAAc,CAAC,CAAS;IAC/B,OAAO,CAAC;SACL,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC;SACxB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;QACxB,kEAAkE;SACjE,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,cAAc,CACnB,IAAI;SACD,OAAO,CAAC,2BAA2B,EAAE,GAAG,CAAC;SACzC,OAAO,CAAC,6BAA6B,EAAE,GAAG,CAAC;SAC3C,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAC5B;SACE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,SAAS,eAAe,CAAC,KAAiB;IACxC,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC1E,MAAM,GAAG,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,gBAAgB;IAC1E,MAAM,GAAG,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,CAAC,KAAa,EAAE,GAAW,EAAU,EAAE,CAClD,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,CAC/C,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,CAC3B,CAAC;IAEJ,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,sCAAsC;IACnE,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,2CAA2C;IACtE,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,+BAA+B;IAE3D,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;IACvB,0DAA0D;IAC1D,IAAI,CAAC,GAAG,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IAE7E,8EAA8E;IAC9E,6EAA6E;IAC7E,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC;IACd,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC;IAC9C,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;YACxB,IAAI,GAAG,CAAC,CAAC;YACT,MAAM;QACR,CAAC;IACH,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC/E,CAAC;IAED,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;IAChC,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAClF,CAAC;IAED,6DAA6D;IAC7D,IAAI,CAAC,GAAG,QAAQ,CAAC;IACjB,IAAI,MAAM,GACR,IAAI,CAAC;IACP,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAC1E,CAAC;QACD,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAC5E,CAAC;QACD,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAC3B,MAAM,cAAc,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACnC,MAAM,OAAO,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7B,MAAM,UAAU,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAC/B,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACxB,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,CAAC;QACzB,IAAI,SAAS,GAAG,OAAO,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACpE,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC;QAClD,IAAI,IAAI,KAAK,mBAAmB,EAAE,CAAC;YACjC,MAAM,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC;YACzC,MAAM;QACR,CAAC;QACD,CAAC,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;IAClD,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;IACJ,CAAC;IAED,0EAA0E;IAC1E,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;IAC/C,IAAI,GAAG,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,OAAO,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,YAAY,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC;IACnC,MAAM,aAAa,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,GAAG,GAAG,EAAE,GAAG,YAAY,GAAG,aAAa,CAAC;IAC1D,IAAI,SAAS,GAAG,cAAc,GAAG,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,cAAc,CAAC,CAAC;IAEnE,sEAAsE;IACtE,IAAI,QAAoB,CAAC;IACzB,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;QACjB,sEAAsE;QACtE,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,EAAE,eAAe,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAChF,CAAC;SAAM,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,QAAQ,GAAG,IAAI,CAAC;IAClB,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,2CAA2C,MAAM,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAExE,4EAA4E;IAC5E,8EAA8E;IAC9E,0EAA0E;IAC1E,MAAM,IAAI,GAAG,cAAc,CACzB,GAAG;SACA,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC;SACzB,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC;SAC1B,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC;SACnC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAC3B;SACE,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC;SACxB,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC;SAC1B,IAAI,EAAE,CAAC;IACV,OAAO,IAAI,CAAC;AACd,CAAC;AAoBD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,IAA6B;IAE7B,MAAM,MAAM,GAAG,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CACvB,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,IAAI,iBAAiB,CAAC,EAC/C,aAAa,CACd,CAAC;IAEF,4EAA4E;IAC5E,IAAI,GAAa,CAAC;IAClB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YACrB,OAAO,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE;YACpD,QAAQ,EAAE,QAAQ;YAClB,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,gBAAgB,CAAC;SAC9C,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,qDAAqD;QACrD,MAAM,GAAG,GAAc;YACrB,IAAI,EAAE,sBAAsB;YAC5B,OAAO,EAAE,yCAAyC,GAAG,KAAM,CAAW,CAAC,OAAO,gEAAgE;YAC9I,SAAS,EAAE,IAAI;YACf,iBAAiB,EAAE,EAAE;YACrB,gBAAgB,EAAE,gBAAgB;SACnC,CAAC;QACF,MAAM,IAAI,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACvB,MAAM,IAAI,gBAAgB,CAAC;YACzB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,0CAA0C,GAAG,8BAA8B;YACpF,SAAS,EAAE,KAAK;YAChB,cAAc,EAAE,GAAG;YACnB,gBAAgB,EAAE,gBAAgB;SACnC,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,qEAAqE;QACrE,+DAA+D;QAC/D,MAAM,IAAI,gBAAgB,CAAC;YACzB,IAAI,EAAE,sBAAsB;YAC5B,OAAO,EAAE,sCAAsC,GAAG,CAAC,MAAM,OAAO,GAAG,gEAAgE;YACnI,SAAS,EAAE,IAAI;YACf,iBAAiB,EAAE,EAAE;YACrB,cAAc,EAAE,GAAG,CAAC,MAAM;YAC1B,gBAAgB,EAAE,gBAAgB;SACnC,CAAC,CAAC;IACL,CAAC;IAED,8EAA8E;IAC9E,6EAA6E;IAC7E,0EAA0E;IAC1E,8EAA8E;IAC9E,0EAA0E;IAC1E,wEAAwE;IACxE,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE;QACtB,IAAI,CAAC;YACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,CAAC,EAAE,CAAC;IACL,4EAA4E;IAC5E,+EAA+E;IAC/E,wEAAwE;IACxE,+EAA+E;IAC/E,iFAAiF;IACjF,gFAAgF;IAChF,4EAA4E;IAC5E,MAAM,cAAc,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,CAAC,CAAS,EAAE,EAAE,CAChC,CAAC,CAAC,QAAQ,CAAC,gBAAgB,CAAC;QAC5B,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACnF,MAAM,WAAW;IACf,kFAAkF;IAClF,kFAAkF;IAClF,CAAC,SAAS,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;QACrC,SAAS,KAAK,SAAS;QACvB,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC;QAC9B,WAAW,CAAC,SAAS,CAAC,CAAC;IACzB,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,gBAAgB,CAAC;YACzB,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,2DAA2D,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,0FAA0F;YACvL,SAAS,EAAE,KAAK;SACjB,CAAC,CAAC;IACL,CAAC;IAED,2EAA2E;IAC3E,0EAA0E;IAC1E,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC;IACpE,IAAI,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,WAAW,GAAG,oBAAoB,EAAE,CAAC;QACvE,MAAM,IAAI,gBAAgB,CAAC;YACzB,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,qBAAqB,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,OAAO,CAAC,yBAAyB,IAAI,CAAC,KAAK,CAAC,oBAAoB,GAAG,OAAO,CAAC,uCAAuC,GAAG,GAAG;YAC/K,SAAS,EAAE,KAAK;YAChB,gBAAgB,EAAE,gBAAgB;SACnC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACpD,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAG,uBAAuB,CAAC,WAAW,CAAC,CAAC;IACtD,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;IAE5B,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC;IAEnC,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,WAAW,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;IAE3D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,IAAI,GAAkB,IAAI,CAAC;IAC/B,IAAI,KAAK,GAAkB,IAAI,CAAC;IAEhC,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,KAAK,CAAC,CAAC;YAC1C,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE;gBAC7D,UAAU,EAAE,IAAI;aACjB,CAAC,CAAC;YACH,KAAK,GAAG,UAAU,CAAC;YACnB,IAAI,GAAG,SAAS,CAAC;QACnB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,0EAA0E;YAC1E,8DAA8D;YAC9D,IAAI,GAAG,IAAI,CAAC;YACZ,KAAK,GAAG,IAAI,CAAC;YACb,KAAK,CAAC,IAAI,CACR,iDAAkD,CAAW,CAAC,OAAO,kGAAkG,GAAG,GAAG,CAC9K,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QAC7B,yEAAyE;QACzE,4EAA4E;QAC5E,2EAA2E;QAC3E,8EAA8E;QAC9E,IAAI,CAAC;YACH,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;YAC9B,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,GAAG,IAAI,CAAC;YACZ,KAAK,GAAG,IAAI,CAAC;YACb,KAAK,CAAC,IAAI,CACR,gCAAiC,CAAW,CAAC,OAAO,2EAA2E,GAAG,GAAG,CACtI,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QAC7B,IAAI,GAAG,SAAS,CAAC,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7E,CAAC;SAAM,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QAC7B,IAAI,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClE,CAAC;SAAM,CAAC;QACN,sCAAsC;QACtC,IAAI,GAAG,IAAI,CAAC;QACZ,KAAK,GAAG,IAAI,CAAC;QACb,KAAK,CAAC,IAAI,CACR,GAAG,MAAM,qEAAqE,GAAG,GAAG,CACrF,CAAC;IACJ,CAAC;IAED,uEAAuE;IACvE,+EAA+E;IAC/E,4EAA4E;IAC5E,4EAA4E;IAC5E,+EAA+E;IAC/E,gFAAgF;IAChF,qDAAqD;IACrD,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QACjB,MAAM,gBAAgB,GACpB,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAClE,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,KAAK,CAAC,IAAI,CACR,MAAM,KAAK,KAAK;gBACd,CAAC,CAAC,2IAA2I,GAAG,gBAAgB;gBAChK,CAAC,CAAC,OAAO,MAAM,qFAAqF,GAAG,GAAG,CAC7G,CAAC;YACF,IAAI,GAAG,IAAI,CAAC;QACd,CAAC;aAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,gBAAgB,GAAG,GAAG,EAAE,CAAC;YAC7D,KAAK,CAAC,IAAI,CACR,OAAO,MAAM,wJAAwJ,GAAG,GAAG,CAC5K,CAAC;YACF,IAAI,GAAG,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,2BAA2B;IAC3B,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,QAAQ,EAAE,CAAC;QAC3C,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC/B,SAAS,GAAG,IAAI,CAAC;QACjB,KAAK,CAAC,IAAI,CACR,8BAA8B,QAAQ,oGAAoG,CAC3I,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,IAAI,IAAI,CAAC;IAC/B,IAAI,SAAS,EAAE,CAAC;QACd,KAAK,CAAC,IAAI,CAAC,oBAAoB,MAAM,GAAG,CAAC,CAAC;IAC5C,CAAC;IAED,MAAM,IAAI,GAAuB;QAC/B,GAAG;QACH,QAAQ;QACR,WAAW;QACX,MAAM;QACN,SAAS;QACT,KAAK;QACL,IAAI;QACJ,SAAS;QACT,SAAS;KACV,CAAC;IAEF,OAAO,QAAQ,CAAC,IAAI,EAAE;QACpB,MAAM,EAAE,8BAA8B;QACtC,WAAW,EAAE,IAAI;QACjB,8EAA8E;QAC9E,QAAQ,EAAE,SAAS,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK;QACrD,SAAS;QACT,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3B,cAAc,EAAE,IAAI;QACpB,iBAAiB,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAC5C,KAAK;KACN,CAAC,CAAC;AACL,CAAC"}
|