@chrischall/mcp-utils 2.11.0 → 2.13.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.
Files changed (118) hide show
  1. package/README.md +507 -13
  2. package/dist/concurrency/index.d.ts +3 -1
  3. package/dist/concurrency/index.d.ts.map +1 -1
  4. package/dist/concurrency/index.js +3 -1
  5. package/dist/concurrency/index.js.map +1 -1
  6. package/dist/concurrency/verify-after-write.d.ts +84 -0
  7. package/dist/concurrency/verify-after-write.d.ts.map +1 -0
  8. package/dist/concurrency/verify-after-write.js +108 -0
  9. package/dist/concurrency/verify-after-write.js.map +1 -0
  10. package/dist/errors/index.d.ts +35 -1
  11. package/dist/errors/index.d.ts.map +1 -1
  12. package/dist/errors/index.js +79 -1
  13. package/dist/errors/index.js.map +1 -1
  14. package/dist/fetchproxy/fallback.d.ts +130 -0
  15. package/dist/fetchproxy/fallback.d.ts.map +1 -0
  16. package/dist/fetchproxy/fallback.js +199 -0
  17. package/dist/fetchproxy/fallback.js.map +1 -0
  18. package/dist/fetchproxy/index.d.ts +31 -1
  19. package/dist/fetchproxy/index.d.ts.map +1 -1
  20. package/dist/fetchproxy/index.js +120 -13
  21. package/dist/fetchproxy/index.js.map +1 -1
  22. package/dist/fs/confine.d.ts +9 -0
  23. package/dist/fs/confine.d.ts.map +1 -1
  24. package/dist/fs/confine.js +11 -2
  25. package/dist/fs/confine.js.map +1 -1
  26. package/dist/fs/index.d.ts +9 -2
  27. package/dist/fs/index.d.ts.map +1 -1
  28. package/dist/fs/index.js +13 -4
  29. package/dist/fs/index.js.map +1 -1
  30. package/dist/fs/magic.d.ts +35 -0
  31. package/dist/fs/magic.d.ts.map +1 -0
  32. package/dist/fs/magic.js +110 -0
  33. package/dist/fs/magic.js.map +1 -0
  34. package/dist/fs/output.d.ts +14 -10
  35. package/dist/fs/output.d.ts.map +1 -1
  36. package/dist/fs/output.js +21 -45
  37. package/dist/fs/output.js.map +1 -1
  38. package/dist/fs/upload-guard.d.ts +97 -0
  39. package/dist/fs/upload-guard.d.ts.map +1 -0
  40. package/dist/fs/upload-guard.js +185 -0
  41. package/dist/fs/upload-guard.js.map +1 -0
  42. package/dist/fs/write.d.ts +100 -0
  43. package/dist/fs/write.d.ts.map +1 -0
  44. package/dist/fs/write.js +207 -0
  45. package/dist/fs/write.js.map +1 -0
  46. package/dist/graphql/client.d.ts +228 -0
  47. package/dist/graphql/client.d.ts.map +1 -0
  48. package/dist/graphql/client.js +341 -0
  49. package/dist/graphql/client.js.map +1 -0
  50. package/dist/graphql/index.d.ts +20 -0
  51. package/dist/graphql/index.d.ts.map +1 -0
  52. package/dist/graphql/index.js +20 -0
  53. package/dist/graphql/index.js.map +1 -0
  54. package/dist/graphql/operation-kind.d.ts +58 -0
  55. package/dist/graphql/operation-kind.d.ts.map +1 -0
  56. package/dist/graphql/operation-kind.js +237 -0
  57. package/dist/graphql/operation-kind.js.map +1 -0
  58. package/dist/html/index.d.ts +33 -0
  59. package/dist/html/index.d.ts.map +1 -1
  60. package/dist/html/index.js +78 -13
  61. package/dist/html/index.js.map +1 -1
  62. package/dist/http/index.d.ts +75 -5
  63. package/dist/http/index.d.ts.map +1 -1
  64. package/dist/http/index.js +111 -22
  65. package/dist/http/index.js.map +1 -1
  66. package/dist/http/net-atoms.d.ts +14 -4
  67. package/dist/http/net-atoms.d.ts.map +1 -1
  68. package/dist/http/net-atoms.js +95 -13
  69. package/dist/http/net-atoms.js.map +1 -1
  70. package/dist/internal/code-point.d.ts +20 -0
  71. package/dist/internal/code-point.d.ts.map +1 -0
  72. package/dist/internal/code-point.js +26 -0
  73. package/dist/internal/code-point.js.map +1 -0
  74. package/dist/internal/retry-after.d.ts +12 -0
  75. package/dist/internal/retry-after.d.ts.map +1 -0
  76. package/dist/internal/retry-after.js +26 -0
  77. package/dist/internal/retry-after.js.map +1 -0
  78. package/dist/response/index.d.ts +1 -0
  79. package/dist/response/index.d.ts.map +1 -1
  80. package/dist/response/index.js +1 -0
  81. package/dist/response/index.js.map +1 -1
  82. package/dist/response/untrusted.d.ts +72 -0
  83. package/dist/response/untrusted.d.ts.map +1 -0
  84. package/dist/response/untrusted.js +55 -0
  85. package/dist/response/untrusted.js.map +1 -0
  86. package/dist/scrape/index.d.ts +78 -1
  87. package/dist/scrape/index.d.ts.map +1 -1
  88. package/dist/scrape/index.js +276 -20
  89. package/dist/scrape/index.js.map +1 -1
  90. package/dist/server/confirm-write.d.ts +152 -0
  91. package/dist/server/confirm-write.d.ts.map +1 -0
  92. package/dist/server/confirm-write.js +107 -0
  93. package/dist/server/confirm-write.js.map +1 -0
  94. package/dist/server/index.d.ts +2 -0
  95. package/dist/server/index.d.ts.map +1 -1
  96. package/dist/server/index.js +2 -0
  97. package/dist/server/index.js.map +1 -1
  98. package/dist/server/merge-write.d.ts +101 -0
  99. package/dist/server/merge-write.d.ts.map +1 -0
  100. package/dist/server/merge-write.js +94 -0
  101. package/dist/server/merge-write.js.map +1 -0
  102. package/dist/session/atomic-write.d.ts +15 -0
  103. package/dist/session/atomic-write.d.ts.map +1 -0
  104. package/dist/session/atomic-write.js +42 -0
  105. package/dist/session/atomic-write.js.map +1 -0
  106. package/dist/session/file-lock.d.ts +57 -0
  107. package/dist/session/file-lock.d.ts.map +1 -0
  108. package/dist/session/file-lock.js +215 -0
  109. package/dist/session/file-lock.js.map +1 -0
  110. package/dist/session/index.d.ts +118 -2
  111. package/dist/session/index.d.ts.map +1 -1
  112. package/dist/session/index.js +200 -82
  113. package/dist/session/index.js.map +1 -1
  114. package/dist/session/login-page.d.ts +73 -0
  115. package/dist/session/login-page.d.ts.map +1 -0
  116. package/dist/session/login-page.js +143 -0
  117. package/dist/session/login-page.js.map +1 -0
  118. package/package.json +5 -1
@@ -0,0 +1,58 @@
1
+ /**
2
+ * GraphQL operation-kind detection — "is this document a query, a mutation or
3
+ * a subscription?" — without a `graphql` dependency.
4
+ *
5
+ * Consolidates three independent answers to the same question
6
+ * (chrischall/fleet-audit#1080, #988, #1020), plus vibo's fourth:
7
+ *
8
+ * - onehome-mcp `src/tools/graphql.ts` `topLevelOperationKinds` — regex-blanks
9
+ * strings and comments, then reads the first word of each depth-0
10
+ * definition. Gates the read-only `onehome_graphql` passthrough tool.
11
+ * - hemnet-mcp `src/transport-fetchproxy.ts` `isReadOnlyOperation` — a char
12
+ * lexer (BOM, commas, `\"""` escapes) that collects EVERY depth-0 name, so a
13
+ * query merely NAMED `mutation` (or carrying an `@mutation` directive) is
14
+ * misread as a write. Gates the bridge's retry-on-timeout.
15
+ * - booli-mcp `src/transport-fetchproxy.ts` `isMutation` — `/\bmutation\b/`,
16
+ * which refuses the timeout retry for any query with a field, alias,
17
+ * argument or comment containing the word.
18
+ * - vibo-mcp `src/client.ts` `isMutation` — `^\s*(#…)*mutation\b`, which
19
+ * misses a mutation preceded by a fragment or a query, so a timed-out
20
+ * write was reported as a safe-to-retry read.
21
+ *
22
+ * This one is a real tokenizer feeding a tiny top-level grammar: strings,
23
+ * block strings (with the spec's `\"""` escape) and comments are consumed as
24
+ * tokens, so their contents can never look like keywords; only the keyword
25
+ * that STARTS each top-level definition is read, so names, directives and
26
+ * variables never count. It is a single linear pass (no regex backtracking),
27
+ * which matters because it runs on caller-supplied documents.
28
+ */
29
+ /**
30
+ * The keyword that starts each top-level definition of a GraphQL document, in
31
+ * order: `'query'`, `'mutation'`, `'subscription'` or `'fragment'` (an
32
+ * anonymous `{ … }` shorthand counts as `'query'`). Anything else at the top
33
+ * level is reported verbatim (`'extend'`, a stray word), so a caller deciding
34
+ * whether to refuse a document sees it rather than an empty list.
35
+ *
36
+ * Best effort on a malformed document — it keeps reading after the first
37
+ * error — so for a yes/no "is this safe" gate prefer
38
+ * {@link isReadOnlyGraphqlDocument}, which also refuses what it cannot parse.
39
+ */
40
+ export declare function graphqlOperationKinds(document: string): string[];
41
+ /**
42
+ * True when executing the document cannot write: it parses cleanly, contains
43
+ * at least one `query` (or `{ … }` shorthand), and every top-level definition
44
+ * is a `query` or a `fragment`. Any `mutation` or `subscription` — wherever it
45
+ * sits, after a fragment or a query included — makes it NOT read-only, and so
46
+ * does anything unrecognised: an empty or comment-only document, an
47
+ * unterminated string, unbalanced braces, an unknown keyword.
48
+ *
49
+ * The bias is deliberate. A wrong "no" costs a refused passthrough call or a
50
+ * skipped timeout retry; a wrong "yes" re-sends a write or lets a read-only
51
+ * tool change account state.
52
+ *
53
+ * The whole document is judged even when an `operationName` would select one
54
+ * operation from it: a document that carries a mutation at all is not handed
55
+ * to a read-only path.
56
+ */
57
+ export declare function isReadOnlyGraphqlDocument(document: string): boolean;
58
+ //# sourceMappingURL=operation-kind.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-kind.d.ts","sourceRoot":"","sources":["../../src/graphql/operation-kind.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAgLH;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAEhE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGnE"}
@@ -0,0 +1,237 @@
1
+ /**
2
+ * GraphQL operation-kind detection — "is this document a query, a mutation or
3
+ * a subscription?" — without a `graphql` dependency.
4
+ *
5
+ * Consolidates three independent answers to the same question
6
+ * (chrischall/fleet-audit#1080, #988, #1020), plus vibo's fourth:
7
+ *
8
+ * - onehome-mcp `src/tools/graphql.ts` `topLevelOperationKinds` — regex-blanks
9
+ * strings and comments, then reads the first word of each depth-0
10
+ * definition. Gates the read-only `onehome_graphql` passthrough tool.
11
+ * - hemnet-mcp `src/transport-fetchproxy.ts` `isReadOnlyOperation` — a char
12
+ * lexer (BOM, commas, `\"""` escapes) that collects EVERY depth-0 name, so a
13
+ * query merely NAMED `mutation` (or carrying an `@mutation` directive) is
14
+ * misread as a write. Gates the bridge's retry-on-timeout.
15
+ * - booli-mcp `src/transport-fetchproxy.ts` `isMutation` — `/\bmutation\b/`,
16
+ * which refuses the timeout retry for any query with a field, alias,
17
+ * argument or comment containing the word.
18
+ * - vibo-mcp `src/client.ts` `isMutation` — `^\s*(#…)*mutation\b`, which
19
+ * misses a mutation preceded by a fragment or a query, so a timed-out
20
+ * write was reported as a safe-to-retry read.
21
+ *
22
+ * This one is a real tokenizer feeding a tiny top-level grammar: strings,
23
+ * block strings (with the spec's `\"""` escape) and comments are consumed as
24
+ * tokens, so their contents can never look like keywords; only the keyword
25
+ * that STARTS each top-level definition is read, so names, directives and
26
+ * variables never count. It is a single linear pass (no regex backtracking),
27
+ * which matters because it runs on caller-supplied documents.
28
+ */
29
+ const NAME_START = /[_A-Za-z]/;
30
+ const NAME_CONTINUE = /[_0-9A-Za-z]/;
31
+ /** Tokenize per the GraphQL lexical grammar, keeping only what the top level needs. */
32
+ function lex(src) {
33
+ const tokens = [];
34
+ const n = src.length;
35
+ let i = 0;
36
+ while (i < n) {
37
+ const c = src[i];
38
+ // Ignored tokens: BOM, whitespace, line terminators, commas.
39
+ if (c === '' || c === ' ' || c === '\t' || c === '\n' || c === '\r' || c === ',') {
40
+ i++;
41
+ continue;
42
+ }
43
+ if (c === '#') {
44
+ while (i < n && src[i] !== '\n' && src[i] !== '\r')
45
+ i++;
46
+ continue;
47
+ }
48
+ if (c === '"') {
49
+ if (src.startsWith('"""', i)) {
50
+ // Block string: ends at the first `"""` that is not the `\"""` escape.
51
+ i += 3;
52
+ for (;;) {
53
+ if (i >= n)
54
+ return { tokens, unterminated: true };
55
+ if (src[i] === '\\' && src.startsWith('"""', i + 1)) {
56
+ i += 4;
57
+ }
58
+ else if (src.startsWith('"""', i)) {
59
+ i += 3;
60
+ break;
61
+ }
62
+ else {
63
+ i++;
64
+ }
65
+ }
66
+ }
67
+ else {
68
+ i++;
69
+ for (;;) {
70
+ if (i >= n || src[i] === '\n' || src[i] === '\r')
71
+ return { tokens, unterminated: true };
72
+ if (src[i] === '\\') {
73
+ i += 2;
74
+ }
75
+ else if (src[i] === '"') {
76
+ i++;
77
+ break;
78
+ }
79
+ else {
80
+ i++;
81
+ }
82
+ }
83
+ }
84
+ tokens.push({ kind: 'string' });
85
+ continue;
86
+ }
87
+ if (NAME_START.test(c)) {
88
+ const start = i;
89
+ i++;
90
+ while (i < n && NAME_CONTINUE.test(src[i]))
91
+ i++;
92
+ tokens.push({ kind: 'name', value: src.slice(start, i) });
93
+ continue;
94
+ }
95
+ tokens.push({ kind: 'punct', value: c });
96
+ i++;
97
+ }
98
+ return { tokens, unterminated: false };
99
+ }
100
+ const OPEN = new Set(['{', '(', '[']);
101
+ const CLOSE = new Set(['}', ')', ']']);
102
+ const OPERATION_KEYWORDS = new Set(['query', 'mutation', 'subscription']);
103
+ /** Read the top-level definitions of a document. */
104
+ function summarize(document) {
105
+ const { tokens, unterminated } = lex(document);
106
+ const kinds = [];
107
+ let wellFormed = !unterminated;
108
+ let i = 0;
109
+ const peek = () => tokens[i];
110
+ const isPunct = (t, value) => t?.kind === 'punct' && t.value === value;
111
+ /**
112
+ * Skip one balanced group starting at an opening bracket. Returns false when
113
+ * the document ends inside it.
114
+ */
115
+ const skipGroup = () => {
116
+ let depth = 0;
117
+ while (i < tokens.length) {
118
+ const t = tokens[i++];
119
+ if (t.kind !== 'punct')
120
+ continue;
121
+ if (OPEN.has(t.value))
122
+ depth++;
123
+ else if (CLOSE.has(t.value)) {
124
+ depth--;
125
+ if (depth === 0)
126
+ return true;
127
+ }
128
+ }
129
+ return false;
130
+ };
131
+ /** `@name` or `@name(args)`, repeated. */
132
+ const skipDirectives = () => {
133
+ while (isPunct(peek(), '@')) {
134
+ i++;
135
+ if (peek()?.kind !== 'name')
136
+ return false;
137
+ i++;
138
+ if (isPunct(peek(), '(') && !skipGroup())
139
+ return false;
140
+ }
141
+ return true;
142
+ };
143
+ /** The selection set that ends every executable definition. */
144
+ const selectionSet = () => isPunct(peek(), '{') && skipGroup();
145
+ while (i < tokens.length) {
146
+ const t = tokens[i];
147
+ if (isPunct(t, '{')) {
148
+ // Anonymous query shorthand.
149
+ kinds.push('query');
150
+ if (!skipGroup())
151
+ wellFormed = false;
152
+ continue;
153
+ }
154
+ if (t.kind !== 'name') {
155
+ // A string, stray punctuation, or a closing bracket with nothing open.
156
+ wellFormed = false;
157
+ i++;
158
+ continue;
159
+ }
160
+ i++;
161
+ kinds.push(t.value);
162
+ let ok;
163
+ if (OPERATION_KEYWORDS.has(t.value)) {
164
+ // query/mutation/subscription Name? VariableDefinitions? Directives? SelectionSet
165
+ if (peek()?.kind === 'name')
166
+ i++;
167
+ ok = true;
168
+ if (isPunct(peek(), '('))
169
+ ok = skipGroup();
170
+ ok = ok && skipDirectives() && selectionSet();
171
+ }
172
+ else if (t.value === 'fragment') {
173
+ // fragment Name on Type Directives? SelectionSet
174
+ ok = peek()?.kind === 'name';
175
+ if (ok)
176
+ i++;
177
+ const on = peek();
178
+ ok = ok && on?.kind === 'name' && on.value === 'on';
179
+ if (ok)
180
+ i++;
181
+ ok = ok && peek()?.kind === 'name';
182
+ if (ok)
183
+ i++;
184
+ ok = ok && skipDirectives() && selectionSet();
185
+ }
186
+ else {
187
+ // Not an executable definition (`extend`, `type`, a typo). Skip to the
188
+ // end of its body so its inner names are not read as definitions.
189
+ ok = false;
190
+ while (i < tokens.length && !isPunct(peek(), '{'))
191
+ i++;
192
+ if (i < tokens.length)
193
+ skipGroup();
194
+ }
195
+ // On a malformed definition the unexpected token is left in place and
196
+ // re-read as the start of the next definition, so a broken head such as
197
+ // `query Q mutation M { … }` cannot hide the mutation behind it.
198
+ if (!ok)
199
+ wellFormed = false;
200
+ }
201
+ return { kinds, wellFormed };
202
+ }
203
+ /**
204
+ * The keyword that starts each top-level definition of a GraphQL document, in
205
+ * order: `'query'`, `'mutation'`, `'subscription'` or `'fragment'` (an
206
+ * anonymous `{ … }` shorthand counts as `'query'`). Anything else at the top
207
+ * level is reported verbatim (`'extend'`, a stray word), so a caller deciding
208
+ * whether to refuse a document sees it rather than an empty list.
209
+ *
210
+ * Best effort on a malformed document — it keeps reading after the first
211
+ * error — so for a yes/no "is this safe" gate prefer
212
+ * {@link isReadOnlyGraphqlDocument}, which also refuses what it cannot parse.
213
+ */
214
+ export function graphqlOperationKinds(document) {
215
+ return summarize(document).kinds;
216
+ }
217
+ /**
218
+ * True when executing the document cannot write: it parses cleanly, contains
219
+ * at least one `query` (or `{ … }` shorthand), and every top-level definition
220
+ * is a `query` or a `fragment`. Any `mutation` or `subscription` — wherever it
221
+ * sits, after a fragment or a query included — makes it NOT read-only, and so
222
+ * does anything unrecognised: an empty or comment-only document, an
223
+ * unterminated string, unbalanced braces, an unknown keyword.
224
+ *
225
+ * The bias is deliberate. A wrong "no" costs a refused passthrough call or a
226
+ * skipped timeout retry; a wrong "yes" re-sends a write or lets a read-only
227
+ * tool change account state.
228
+ *
229
+ * The whole document is judged even when an `operationName` would select one
230
+ * operation from it: a document that carries a mutation at all is not handed
231
+ * to a read-only path.
232
+ */
233
+ export function isReadOnlyGraphqlDocument(document) {
234
+ const { kinds, wellFormed } = summarize(document);
235
+ return wellFormed && kinds.includes('query') && kinds.every((k) => k === 'query' || k === 'fragment');
236
+ }
237
+ //# sourceMappingURL=operation-kind.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-kind.js","sourceRoot":"","sources":["../../src/graphql/operation-kind.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,9 @@
15
15
  * escaped strings.
16
16
  * - `extractPlainTextFromHtml` — dependency-free script/style strip + entity
17
17
  * decode used to render Infinite Campus message bodies as plain text.
18
+ * - `htmlToReadableText` — the block-aware DOM text extractor (onthecheap):
19
+ * block boundaries become word breaks, inline markup stays joined, code
20
+ * elements are dropped, every entity is decoded.
18
21
  * - `urlToPath` / `locationToSlug` / `buildIdExtractor` — the small URL atoms
19
22
  * that were byte-identical across the cohort.
20
23
  */
@@ -98,6 +101,36 @@ export declare function extractJsonFromHtml(html: string, marker?: string): Reco
98
101
  * @returns The decoded, whitespace-collapsed plain text (`''` for empty input).
99
102
  */
100
103
  export declare function extractPlainTextFromHtml(html: string): string;
104
+ /** Options for {@link htmlToReadableText}. */
105
+ export interface ReadableTextOptions {
106
+ /**
107
+ * Cap the result at this many characters (plus a trailing `…`). The cut
108
+ * lands on the last word boundary when that keeps at least 60% of the
109
+ * budget, otherwise mid-word. Omit for no cap.
110
+ */
111
+ limit?: number;
112
+ }
113
+ /**
114
+ * Render HTML as readable plain text, the way a browser would lay the words
115
+ * out: markup stripped, every HTML entity decoded, whitespace collapsed.
116
+ *
117
+ * Unlike {@link extractPlainTextFromHtml} (the dependency-free regex version,
118
+ * kept unchanged for its callers) this walks the DOM, so:
119
+ * - **block** boundaries (`<p>`, `<div>`, `<li>`, `<td>`, `<br>`, headings, …)
120
+ * become word breaks — `<p>One</p><p>Two</p>` → `One Two`, not `OneTwo`;
121
+ * - **inline** markup stays joined — `<b>F</b>ree` → `Free`, not `F ree`;
122
+ * - `<script>`/`<style>` (incl. JSON-LD), `<noscript>`, `<template>`,
123
+ * `<iframe>` and `<svg>` content is dropped, and comments are ignored;
124
+ * - the full named-entity table is decoded (`&eacute;`, `&ndash;`, …).
125
+ *
126
+ * Use it for article / post / message bodies. Iterative, so hostile nesting
127
+ * depth cannot overflow the stack. Hoisted from onthecheap-mcp's `htmlToText`
128
+ * (fleet audit #1083).
129
+ *
130
+ * @param html The HTML (fragment or full document). `null`/`undefined` → `''`.
131
+ * @returns The readable text (`''` for empty input).
132
+ */
133
+ export declare function htmlToReadableText(html: string | null | undefined, opts?: ReadableTextOptions): string;
101
134
  /**
102
135
  * Reduce a portal URL (or path) to its `pathname + search` portion.
103
136
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/html/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAS,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG3D,YAAY,EAAE,WAAW,EAAE,CAAC;AAE5B,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B,2DAA2D;IAC3D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,4EAA4E;IAC5E,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;CAClB;AA4CD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,GAAG,WAAW,EAC1B,OAAO,EAAE,MAAM,GACd,aAAa,GAAG,IAAI,CAetB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,GAAG,WAAW,EAC1B,OAAO,EAAE,MAAM,EACf,QAAQ,SAAM,GACb,WAAW,EAAE,CAgBf;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,MAAM,SAAsB,GAC3B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAkDhC;AAYD;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqB7D;AAmDD;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO/C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOpD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,GACZ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,KAAK,MAAM,GAAG,SAAS,CAU1D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/html/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAmB,KAAK,WAAW,EAA4B,MAAM,kBAAkB,CAAC;AAI/F,YAAY,EAAE,WAAW,EAAE,CAAC;AAE5B,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B,2DAA2D;IAC3D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,4EAA4E;IAC5E,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;CAClB;AA4CD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,GAAG,WAAW,EAC1B,OAAO,EAAE,MAAM,GACd,aAAa,GAAG,IAAI,CAetB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,GAAG,WAAW,EAC1B,OAAO,EAAE,MAAM,EACf,QAAQ,SAAM,GACb,WAAW,EAAE,CAgBf;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,MAAM,SAAsB,GAC3B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAkDhC;AAYD;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqB7D;AAwDD,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,GAAE,mBAAwB,GAAG,MAAM,CAmC1G;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO/C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOpD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,GACZ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,KAAK,MAAM,GAAG,SAAS,CAU1D"}
@@ -15,11 +15,15 @@
15
15
  * escaped strings.
16
16
  * - `extractPlainTextFromHtml` — dependency-free script/style strip + entity
17
17
  * decode used to render Infinite Campus message bodies as plain text.
18
+ * - `htmlToReadableText` — the block-aware DOM text extractor (onthecheap):
19
+ * block boundaries become word breaks, inline markup stays joined, code
20
+ * elements are dropped, every entity is decoded.
18
21
  * - `urlToPath` / `locationToSlug` / `buildIdExtractor` — the small URL atoms
19
22
  * that were byte-identical across the cohort.
20
23
  */
21
- import { parse } from 'node-html-parser';
24
+ import { parse, NodeType } from 'node-html-parser';
22
25
  import { asciiLower } from '../internal/ascii.js';
26
+ import { codePointOr } from '../internal/code-point.js';
23
27
  /** Normalise an element's text: collapse internal whitespace runs and trim. */
24
28
  function cellText(el) {
25
29
  return el.text.replace(/\s+/g, ' ').trim();
@@ -289,21 +293,82 @@ function stripElementContent(html, tag) {
289
293
  i = closeAt + close.length;
290
294
  }
291
295
  }
296
+ // ---------------------------------------------------------------------------
297
+ // htmlToReadableText
298
+ // ---------------------------------------------------------------------------
299
+ /** Elements whose content is code or data, never readable text. */
300
+ const NON_TEXT_TAGS = new Set(['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEMPLATE', 'IFRAME', 'SVG']);
292
301
  /**
293
- * `String.fromCodePoint` guarded against a `RangeError` on an out-of-range
294
- * (negative or > 0x10FFFF) code point from a scraped numeric entity — returns
295
- * the raw entity text unchanged instead of throwing. (Lone surrogates do NOT
296
- * throw and pass the guard, yielding a lone-surrogate string; harmless here.)
302
+ * Elements that start a new line when rendered. Their boundaries become a word
303
+ * break; inline elements (`<b>`, `<a>`, …) do not, so `<b>F</b>ree` stays one
304
+ * word.
297
305
  */
298
- function codePointOr(code, raw) {
299
- if (!Number.isInteger(code) || code < 0 || code > 0x10ffff)
300
- return raw;
301
- try {
302
- return String.fromCodePoint(code);
303
- }
304
- catch {
305
- return raw;
306
+ const BREAK_TAGS = new Set([
307
+ 'ADDRESS', 'ARTICLE', 'ASIDE', 'BLOCKQUOTE', 'BR', 'DD', 'DIV', 'DL', 'DT',
308
+ 'FIGCAPTION', 'FIGURE', 'FOOTER', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEADER',
309
+ 'HR', 'LI', 'MAIN', 'NAV', 'OL', 'P', 'PRE', 'SECTION', 'TABLE', 'TD', 'TH',
310
+ 'TR', 'UL',
311
+ ]);
312
+ /**
313
+ * Render HTML as readable plain text, the way a browser would lay the words
314
+ * out: markup stripped, every HTML entity decoded, whitespace collapsed.
315
+ *
316
+ * Unlike {@link extractPlainTextFromHtml} (the dependency-free regex version,
317
+ * kept unchanged for its callers) this walks the DOM, so:
318
+ * - **block** boundaries (`<p>`, `<div>`, `<li>`, `<td>`, `<br>`, headings, …)
319
+ * become word breaks — `<p>One</p><p>Two</p>` → `One Two`, not `OneTwo`;
320
+ * - **inline** markup stays joined — `<b>F</b>ree` → `Free`, not `F ree`;
321
+ * - `<script>`/`<style>` (incl. JSON-LD), `<noscript>`, `<template>`,
322
+ * `<iframe>` and `<svg>` content is dropped, and comments are ignored;
323
+ * - the full named-entity table is decoded (`&eacute;`, `&ndash;`, …).
324
+ *
325
+ * Use it for article / post / message bodies. Iterative, so hostile nesting
326
+ * depth cannot overflow the stack. Hoisted from onthecheap-mcp's `htmlToText`
327
+ * (fleet audit #1083).
328
+ *
329
+ * @param html The HTML (fragment or full document). `null`/`undefined` → `''`.
330
+ * @returns The readable text (`''` for empty input).
331
+ */
332
+ export function htmlToReadableText(html, opts = {}) {
333
+ if (!html)
334
+ return '';
335
+ const parts = [];
336
+ // Explicit stack instead of recursion; a `string` entry is a pending break
337
+ // to emit after an element's children.
338
+ // node-html-parser surfaces a leading `<!doctype …>` as a TEXT node, so it
339
+ // would read as words; drop it before parsing (anchored, so linear).
340
+ const stack = [parse(html.replace(/^\s*<!doctype[^>]*>/i, ''))];
341
+ while (stack.length > 0) {
342
+ const item = stack.pop();
343
+ if (typeof item === 'string') {
344
+ parts.push(item);
345
+ continue;
346
+ }
347
+ if (item.nodeType === NodeType.TEXT_NODE) {
348
+ parts.push(item.text); // `.text` is entity-decoded
349
+ continue;
350
+ }
351
+ if (item.nodeType !== NodeType.ELEMENT_NODE)
352
+ continue; // comments
353
+ const tag = item.tagName ?? '';
354
+ if (NON_TEXT_TAGS.has(tag))
355
+ continue;
356
+ const breaks = BREAK_TAGS.has(tag);
357
+ if (breaks) {
358
+ parts.push(' ');
359
+ stack.push(' ');
360
+ }
361
+ const children = item.childNodes;
362
+ for (let i = children.length - 1; i >= 0; i--)
363
+ stack.push(children[i]);
306
364
  }
365
+ const text = parts.join('').replace(/\s+/g, ' ').trim();
366
+ const { limit } = opts;
367
+ if (limit === undefined || text.length <= limit)
368
+ return text;
369
+ const cut = text.slice(0, limit);
370
+ const boundary = cut.lastIndexOf(' ');
371
+ return `${(boundary > limit * 0.6 ? cut.slice(0, boundary) : cut).trimEnd()}…`;
307
372
  }
308
373
  /**
309
374
  * Reduce a portal URL (or path) to its `pathname + search` portion.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/html/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/html/index.ts"],"names":[],"mappings":"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"}
@@ -112,9 +112,13 @@ export interface ApiClientOptions {
112
112
  onUnauthorized?: () => Error;
113
113
  /**
114
114
  * Override the error thrown when a 429 persists past the retry budget.
115
- * Defaults to {@link RateLimitedError}.
115
+ * Defaults to {@link RateLimitedError}. Receives a {@link RateLimitContext}
116
+ * describing that final 429 — its parsed `Retry-After` and whether a CDN/WAF
117
+ * page answered it — read before the body is discarded, so a consumer no
118
+ * longer has to capture the response at the `fetchImpl` seam (musicbrainz,
119
+ * viator). A zero-argument hook still works.
116
120
  */
117
- onRateLimited?: () => Error;
121
+ onRateLimited?: (ctx: RateLimitContext) => Error;
118
122
  /** Injectable fetch (for tests). Defaults to the global `fetch`. */
119
123
  fetchImpl?: typeof fetch;
120
124
  /** Injectable sleep (for tests). Defaults to `setTimeout`. */
@@ -129,6 +133,36 @@ export interface ApiClientOptions {
129
133
  */
130
134
  timeout?: number;
131
135
  }
136
+ /**
137
+ * What {@link ApiClientOptions.onRateLimited} is told about the 429 that
138
+ * exhausted the retry budget (the LAST response, not the first).
139
+ */
140
+ export interface RateLimitContext {
141
+ /** The response status (429). */
142
+ status: number;
143
+ /** The raw `Retry-After` header, or `null` when absent. */
144
+ retryAfter: string | null;
145
+ /**
146
+ * The wait `Retry-After` asks for, in ms: delta-seconds or an HTTP-date
147
+ * (clamped at 0 once past). Uncapped — `maxRetryAfterMs` bounds the client's
148
+ * own sleep, not this report. `undefined` when absent or unparseable.
149
+ */
150
+ retryAfterMs: number | undefined;
151
+ /**
152
+ * The CDN/WAF that answered, per {@link detectEdgeBlock}, or `null`. A 429
153
+ * can be an edge refusal page rather than the API's rate limit; return an
154
+ * {@link EdgeBlockedError} for it to say so. A JSON body is the API's own
155
+ * answer and is not read (`cf-mitigated` is still honoured); any other body
156
+ * is read under the request timeout, and an unreadable one is no evidence.
157
+ */
158
+ edgeBlock: {
159
+ vendor: string;
160
+ } | null;
161
+ /** The request method, as passed. */
162
+ method: string;
163
+ /** The request path, as passed. */
164
+ path: string;
165
+ }
132
166
  /** A request body and/or extra headers for a single call. */
133
167
  export interface RequestOptions {
134
168
  /** JSON-serialized into the request body when present. */
@@ -139,6 +173,27 @@ export interface RequestOptions {
139
173
  * precedence over {@link body} when both are present.
140
174
  */
141
175
  formData?: FormData;
176
+ /**
177
+ * Form-encoded body, sent as `application/x-www-form-urlencoded;charset=UTF-8`
178
+ * (the same type `fetch` gives a bare `URLSearchParams`). A plain record is
179
+ * encoded with `URLSearchParams`, skipping `undefined` values. Consolidates
180
+ * resy's local `URLSearchParams` branch (its login, favourite, notify and
181
+ * booking writes are all form posts).
182
+ */
183
+ form?: URLSearchParams | Record<string, string | number | boolean | undefined>;
184
+ /**
185
+ * A string body sent verbatim — for upstreams whose writes are neither JSON
186
+ * nor a form, e.g. MusicBrainz's `application/xml` submissions. Its
187
+ * `Content-Type` is {@link contentType}, defaulting to
188
+ * `text/plain; charset=utf-8`. An empty string is still a body.
189
+ */
190
+ rawBody?: string;
191
+ /**
192
+ * The `Content-Type` of {@link rawBody}. Only valid alongside `rawBody` —
193
+ * the other body kinds set their own (a per-request `headers` entry still
194
+ * overrides any of them).
195
+ */
196
+ contentType?: string;
142
197
  /** Extra request headers, merged over the defaults. */
143
198
  headers?: Record<string, string>;
144
199
  /** Query params appended via {@link buildQueryString} when present. */
@@ -180,10 +235,15 @@ export declare class UnauthorizedError extends Error {
180
235
  readonly status = 401;
181
236
  constructor(service: string);
182
237
  }
183
- /** Thrown when a 429 persists after the retry budget is exhausted. */
238
+ /**
239
+ * Thrown when a 429 persists after the retry budget is exhausted.
240
+ * `retryAfterMs` is the final response's `Retry-After` as a wait in ms (see
241
+ * {@link RateLimitContext.retryAfterMs}), when it sent a parseable one.
242
+ */
184
243
  export declare class RateLimitedError extends Error {
185
244
  readonly status = 429;
186
- constructor(service: string);
245
+ readonly retryAfterMs: number | undefined;
246
+ constructor(service: string, retryAfterMs?: number);
187
247
  }
188
248
  /** Thrown when a request exceeds {@link ApiClientOptions.timeout}. */
189
249
  export declare class RequestTimeoutError extends Error {
@@ -497,6 +557,14 @@ export interface RunBoundedBatchOptions<T, R> {
497
557
  setTimer?: (ms: number, cb: () => void) => unknown;
498
558
  /** Injectable clear paired with {@link setTimer}. Defaults to `clearTimeout`. */
499
559
  clearTimer?: (handle: unknown) => void;
560
+ /**
561
+ * The caller's cancellation. Defaults to the running tool call's
562
+ * ({@link currentCallSignal}). When it aborts the batch answers AT ONCE, as if
563
+ * the deadline had fired: unsettled slots are backfilled by {@link onTimeout},
564
+ * workers' signals abort, and nothing more is dispatched — a client that
565
+ * cancelled is no longer fetched for.
566
+ */
567
+ signal?: AbortSignal;
500
568
  }
501
569
  /**
502
570
  * Run `worker` over `items` with an overall hard deadline and a per-item
@@ -510,7 +578,9 @@ export interface RunBoundedBatchOptions<T, R> {
510
578
  * awaited, so a single hung row can't keep the whole call (and the MCP request
511
579
  * deadline behind it) pinned open. Queued items not yet started are never
512
580
  * dispatched after the deadline; a worker that retries or loops internally
513
- * should still check `signal.aborted` itself to stop mid-item. When everything settles before the deadline,
581
+ * should still check `signal.aborted` itself to stop mid-item. The caller's
582
+ * cancellation ({@link RunBoundedBatchOptions.signal}, the running tool call's
583
+ * by default) ends the batch the same way, immediately. When everything settles before the deadline,
514
584
  * the timer is cleared and every slot holds its real result.
515
585
  *
516
586
  * Generalises zillow's bulk-tool `runWithDeadline` (`zillow_bulk_get` /