@traffic-twin/sdk 0.0.0-stage → 0.1.1

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 (133) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +171 -2
  3. package/dist/cjs/agent.d.ts +143 -0
  4. package/dist/cjs/agent.js +927 -0
  5. package/dist/cjs/capture/body.d.ts +11 -0
  6. package/dist/cjs/capture/body.js +73 -0
  7. package/dist/cjs/capture/common.d.ts +75 -0
  8. package/dist/cjs/capture/common.js +180 -0
  9. package/dist/cjs/capture/context.d.ts +28 -0
  10. package/dist/cjs/capture/context.js +92 -0
  11. package/dist/cjs/capture/inbound.d.ts +12 -0
  12. package/dist/cjs/capture/inbound.js +195 -0
  13. package/dist/cjs/capture/outbound-http.d.ts +4 -0
  14. package/dist/cjs/capture/outbound-http.js +200 -0
  15. package/dist/cjs/capture/outbound-undici.d.ts +2 -0
  16. package/dist/cjs/capture/outbound-undici.js +242 -0
  17. package/dist/cjs/capture/wire.d.ts +27 -0
  18. package/dist/cjs/capture/wire.js +68 -0
  19. package/dist/cjs/export/event.d.ts +46 -0
  20. package/dist/cjs/export/event.js +91 -0
  21. package/dist/cjs/export/exporter.d.ts +50 -0
  22. package/dist/cjs/export/exporter.js +192 -0
  23. package/dist/cjs/export/pipeline.d.ts +20 -0
  24. package/dist/cjs/export/pipeline.js +40 -0
  25. package/dist/cjs/fleet/asn1.d.ts +44 -0
  26. package/dist/cjs/fleet/asn1.js +150 -0
  27. package/dist/cjs/fleet/client.d.ts +84 -0
  28. package/dist/cjs/fleet/client.js +216 -0
  29. package/dist/cjs/fleet/http.d.ts +17 -0
  30. package/dist/cjs/fleet/http.js +80 -0
  31. package/dist/cjs/fleet/store.d.ts +18 -0
  32. package/dist/cjs/fleet/store.js +115 -0
  33. package/dist/cjs/index.d.ts +14 -0
  34. package/dist/cjs/index.js +69 -0
  35. package/dist/cjs/log.d.ts +4 -0
  36. package/dist/cjs/log.js +30 -0
  37. package/dist/cjs/package.json +1 -0
  38. package/dist/cjs/register.d.ts +1 -0
  39. package/dist/cjs/register.js +18 -0
  40. package/dist/cjs/sanitizer/bodies.d.ts +55 -0
  41. package/dist/cjs/sanitizer/bodies.js +710 -0
  42. package/dist/cjs/sanitizer/formats.d.ts +26 -0
  43. package/dist/cjs/sanitizer/formats.js +110 -0
  44. package/dist/cjs/sanitizer/graphql.d.ts +23 -0
  45. package/dist/cjs/sanitizer/graphql.js +478 -0
  46. package/dist/cjs/sanitizer/hostpat.d.ts +15 -0
  47. package/dist/cjs/sanitizer/hostpat.js +129 -0
  48. package/dist/cjs/sanitizer/json.d.ts +60 -0
  49. package/dist/cjs/sanitizer/json.js +460 -0
  50. package/dist/cjs/sanitizer/policy.d.ts +224 -0
  51. package/dist/cjs/sanitizer/policy.js +820 -0
  52. package/dist/cjs/sanitizer/sanitizer.d.ts +149 -0
  53. package/dist/cjs/sanitizer/sanitizer.js +1473 -0
  54. package/dist/cjs/sanitizer/utf8.d.ts +22 -0
  55. package/dist/cjs/sanitizer/utf8.js +180 -0
  56. package/dist/cjs/sanitizer/vectors.d.ts +22 -0
  57. package/dist/cjs/sanitizer/vectors.js +130 -0
  58. package/dist/cjs/version.d.ts +2 -0
  59. package/dist/cjs/version.js +5 -0
  60. package/dist/cjs/worker/dir.d.ts +1 -0
  61. package/dist/cjs/worker/dir.js +8 -0
  62. package/dist/cjs/worker/pool.d.ts +47 -0
  63. package/dist/cjs/worker/pool.js +179 -0
  64. package/dist/cjs/worker/protocol.d.ts +37 -0
  65. package/dist/cjs/worker/protocol.js +3 -0
  66. package/dist/cjs/worker/sanitizer-worker.d.ts +1 -0
  67. package/dist/cjs/worker/sanitizer-worker.js +53 -0
  68. package/dist/esm/agent.d.ts +143 -0
  69. package/dist/esm/agent.js +921 -0
  70. package/dist/esm/capture/body.d.ts +11 -0
  71. package/dist/esm/capture/body.js +69 -0
  72. package/dist/esm/capture/common.d.ts +75 -0
  73. package/dist/esm/capture/common.js +166 -0
  74. package/dist/esm/capture/context.d.ts +28 -0
  75. package/dist/esm/capture/context.js +84 -0
  76. package/dist/esm/capture/inbound.d.ts +12 -0
  77. package/dist/esm/capture/inbound.js +187 -0
  78. package/dist/esm/capture/outbound-http.d.ts +4 -0
  79. package/dist/esm/capture/outbound-http.js +193 -0
  80. package/dist/esm/capture/outbound-undici.d.ts +2 -0
  81. package/dist/esm/capture/outbound-undici.js +236 -0
  82. package/dist/esm/capture/wire.d.ts +27 -0
  83. package/dist/esm/capture/wire.js +65 -0
  84. package/dist/esm/export/event.d.ts +46 -0
  85. package/dist/esm/export/event.js +85 -0
  86. package/dist/esm/export/exporter.d.ts +50 -0
  87. package/dist/esm/export/exporter.js +188 -0
  88. package/dist/esm/export/pipeline.d.ts +20 -0
  89. package/dist/esm/export/pipeline.js +37 -0
  90. package/dist/esm/fleet/asn1.d.ts +44 -0
  91. package/dist/esm/fleet/asn1.js +128 -0
  92. package/dist/esm/fleet/client.d.ts +84 -0
  93. package/dist/esm/fleet/client.js +204 -0
  94. package/dist/esm/fleet/http.d.ts +17 -0
  95. package/dist/esm/fleet/http.js +73 -0
  96. package/dist/esm/fleet/store.d.ts +18 -0
  97. package/dist/esm/fleet/store.js +110 -0
  98. package/dist/esm/index.d.ts +14 -0
  99. package/dist/esm/index.js +48 -0
  100. package/dist/esm/log.d.ts +4 -0
  101. package/dist/esm/log.js +25 -0
  102. package/dist/esm/package.json +1 -0
  103. package/dist/esm/register.d.ts +1 -0
  104. package/dist/esm/register.js +16 -0
  105. package/dist/esm/sanitizer/bodies.d.ts +55 -0
  106. package/dist/esm/sanitizer/bodies.js +694 -0
  107. package/dist/esm/sanitizer/formats.d.ts +26 -0
  108. package/dist/esm/sanitizer/formats.js +101 -0
  109. package/dist/esm/sanitizer/graphql.d.ts +23 -0
  110. package/dist/esm/sanitizer/graphql.js +472 -0
  111. package/dist/esm/sanitizer/hostpat.d.ts +15 -0
  112. package/dist/esm/sanitizer/hostpat.js +122 -0
  113. package/dist/esm/sanitizer/json.d.ts +60 -0
  114. package/dist/esm/sanitizer/json.js +447 -0
  115. package/dist/esm/sanitizer/policy.d.ts +224 -0
  116. package/dist/esm/sanitizer/policy.js +800 -0
  117. package/dist/esm/sanitizer/sanitizer.d.ts +149 -0
  118. package/dist/esm/sanitizer/sanitizer.js +1459 -0
  119. package/dist/esm/sanitizer/utf8.d.ts +22 -0
  120. package/dist/esm/sanitizer/utf8.js +166 -0
  121. package/dist/esm/sanitizer/vectors.d.ts +22 -0
  122. package/dist/esm/sanitizer/vectors.js +125 -0
  123. package/dist/esm/version.d.ts +2 -0
  124. package/dist/esm/version.js +2 -0
  125. package/dist/esm/worker/dir.d.ts +1 -0
  126. package/dist/esm/worker/dir.js +5 -0
  127. package/dist/esm/worker/pool.d.ts +47 -0
  128. package/dist/esm/worker/pool.js +175 -0
  129. package/dist/esm/worker/protocol.d.ts +37 -0
  130. package/dist/esm/worker/protocol.js +2 -0
  131. package/dist/esm/worker/sanitizer-worker.d.ts +1 -0
  132. package/dist/esm/worker/sanitizer-worker.js +51 -0
  133. package/package.json +75 -4
@@ -0,0 +1,710 @@
1
+ "use strict";
2
+ // Body formats of spec v1.19 (sanitizer.md 22): XML and SOAP (22.2), multipart/form-data
3
+ // (22.3), base64 documents and the document size classes (22.4). Pure functions: they never
4
+ // see the policy. Mirrors conformance/ref/bodies.py.
5
+ //
6
+ // The XML reader is a hand-written strict subset of XML 1.0 + Namespaces: no DTD, no entity
7
+ // other than the five predefined ones and character references, never a platform XML library
8
+ // (hardening: XXE and entity expansion are impossible by construction).
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.InvalidXml = exports.XML_MAX_NESTING = exports.SOAP12 = exports.SOAP11 = exports.NS_XML = exports.InvalidMultipart = void 0;
11
+ exports.docClass = docClass;
12
+ exports.magicKind = magicKind;
13
+ exports.base64Doc = base64Doc;
14
+ exports.mediaParams = mediaParams;
15
+ exports.parseMultipart = parseMultipart;
16
+ exports.fileToken = fileToken;
17
+ exports.parseXml = parseXml;
18
+ exports.xmlValue = xmlValue;
19
+ exports.nsPattern = nsPattern;
20
+ exports.xmlNamespaces = xmlNamespaces;
21
+ exports.soapActionName = soapActionName;
22
+ const utf8_js_1 = require("./utf8.js");
23
+ // ------------------------------------------------------------------ documents (22.4)
24
+ /** 22.4: document class on the decoded length. */
25
+ function docClass(n) {
26
+ if (n <= 1024)
27
+ return 'l1k';
28
+ if (n <= 16384)
29
+ return 'l16k';
30
+ if (n <= 131072)
31
+ return 'l128k';
32
+ if (n <= 1048576)
33
+ return 'l1m';
34
+ if (n <= 8388608)
35
+ return 'l8m';
36
+ return 'lmax';
37
+ }
38
+ const PNG = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
39
+ const OLE = Buffer.from([0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1]);
40
+ function startsWith(b, prefix, at = 0) {
41
+ const n = prefix.length;
42
+ if (b.length < at + n)
43
+ return false;
44
+ for (let i = 0; i < n; i++) {
45
+ const c = typeof prefix === 'string' ? prefix.charCodeAt(i) : prefix[i];
46
+ if (b[at + i] !== c)
47
+ return false;
48
+ }
49
+ return true;
50
+ }
51
+ /** 22.4 point 3 without the zip refinement: kind from the first 12 bytes; 'zip?' for a zip. */
52
+ function headKind(b) {
53
+ if (startsWith(b, '%PDF-'))
54
+ return 'pdf';
55
+ if (startsWith(b, PNG))
56
+ return 'png';
57
+ if (b.length >= 3 && b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff)
58
+ return 'jpeg';
59
+ if (startsWith(b, 'GIF87a') || startsWith(b, 'GIF89a'))
60
+ return 'gif';
61
+ if (startsWith(b, 'RIFF') && startsWith(b, 'WEBP', 8))
62
+ return 'webp';
63
+ if (startsWith(b, OLE))
64
+ return 'ole';
65
+ if (startsWith(b, 'PK\x03\x04'))
66
+ return 'zip?';
67
+ return null;
68
+ }
69
+ function zipKind(b) {
70
+ if (b.indexOf('word/document.xml', 0, 'latin1') >= 0)
71
+ return 'docx';
72
+ if (b.indexOf('xl/workbook.xml', 0, 'latin1') >= 0)
73
+ return 'xlsx';
74
+ if (b.indexOf('ppt/presentation.xml', 0, 'latin1') >= 0)
75
+ return 'pptx';
76
+ return 'zip';
77
+ }
78
+ /** 22.4 point 3: kind of a document from its bytes, or null. */
79
+ function magicKind(b) {
80
+ const k = headKind(b);
81
+ return k === 'zip?' ? zipKind(b) : k;
82
+ }
83
+ const B64_RE = /^[A-Za-z0-9+/_-]*={0,2}$/;
84
+ const DATA_PREFIX_RE = /^data:[!-+\--~]*;base64$/; // printable ASCII without ','
85
+ const DOC_MIN_CHARS = 16;
86
+ /** 22.4: 'drop:doc:<kind>:<class>' if the string is the base64 of a document, else null. */
87
+ function base64Doc(str) {
88
+ let s = str;
89
+ if (s.startsWith('data:')) {
90
+ const i = s.indexOf(',');
91
+ if (i < 0 || !DATA_PREFIX_RE.test(s.slice(0, i)))
92
+ return null;
93
+ s = s.slice(i + 1);
94
+ }
95
+ if (s.length < DOC_MIN_CHARS || !B64_RE.test(s))
96
+ return null;
97
+ let end = s.length;
98
+ while (end > 0 && s.charCodeAt(end - 1) === 0x3d)
99
+ end--;
100
+ if (end !== s.length && s.length % 4 !== 0)
101
+ return null;
102
+ if (end % 4 === 1)
103
+ return null;
104
+ const body = s.slice(0, end);
105
+ // The first 16 characters give the first 12 bytes: enough for every magic number but
106
+ // the zip names, which need the whole document.
107
+ const head = Buffer.from(body.slice(0, 16), 'base64url');
108
+ let kind = headKind(head);
109
+ if (kind === null)
110
+ return null;
111
+ const length = Math.floor((end * 3) / 4);
112
+ if (kind === 'zip?')
113
+ kind = zipKind(Buffer.from(body, 'base64url'));
114
+ return 'drop:doc:' + kind + ':' + docClass(length);
115
+ }
116
+ // ------------------------------------------------------------------ media type parameters (22.2, 22.3)
117
+ function trimSpTab(s) {
118
+ let a = 0;
119
+ let b = s.length;
120
+ while (a < b && (s[a] === ' ' || s[a] === '\t'))
121
+ a++;
122
+ while (b > a && (s[b - 1] === ' ' || s[b - 1] === '\t'))
123
+ b--;
124
+ return s.slice(a, b);
125
+ }
126
+ function asciiLowerStr(s) {
127
+ return s.replace(/[A-Z]+/g, (m) => m.toLowerCase());
128
+ }
129
+ /**
130
+ * Parameters of a normalized content-type value: lowercase name -> value, first occurrence
131
+ * wins. Split on ';', each piece trimmed and split on the first '='; a value starting with '"'
132
+ * must end with '"' (length >= 2) and loses both quotes, no escapes. Not UTF-8: no parameters.
133
+ */
134
+ function mediaParams(value) {
135
+ const out = new Map();
136
+ if (!value)
137
+ return out;
138
+ const s = (0, utf8_js_1.decodeUtf8)(value);
139
+ if (s === null)
140
+ return out;
141
+ const pieces = s.split(';');
142
+ for (let i = 1; i < pieces.length; i++) {
143
+ const piece = trimSpTab(pieces[i]);
144
+ const eq = piece.indexOf('=');
145
+ if (eq < 0)
146
+ continue;
147
+ const name = asciiLowerStr(trimSpTab(piece.slice(0, eq)));
148
+ let v = trimSpTab(piece.slice(eq + 1));
149
+ if (v.startsWith('"')) {
150
+ if (v.length < 2 || !v.endsWith('"'))
151
+ continue;
152
+ v = v.slice(1, -1);
153
+ }
154
+ if (!out.has(name))
155
+ out.set(name, v);
156
+ }
157
+ return out;
158
+ }
159
+ // ------------------------------------------------------------------ multipart (22.3)
160
+ const BOUNDARY_RE = /^[0-9A-Za-z'()+_,./:=? -]{0,69}[0-9A-Za-z'()+_,./:=?-]$/;
161
+ const PART_HEADER_NAME_RE = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;
162
+ const FILE_TYPES = new Map([
163
+ ['text/plain', 'text'], ['text/csv', 'csv'], ['application/json', 'json'], ['application/xml', 'xml'], ['text/xml', 'xml'],
164
+ ]);
165
+ const CRLF = Buffer.from('\r\n');
166
+ const CRLFCRLF = Buffer.from('\r\n\r\n');
167
+ class InvalidMultipart extends Error {
168
+ }
169
+ exports.InvalidMultipart = InvalidMultipart;
170
+ const bad = (why) => {
171
+ throw new InvalidMultipart(why);
172
+ };
173
+ /** content-disposition of a part -> params (first wins); InvalidMultipart if not form-data. */
174
+ function disposition(s) {
175
+ const i = s.indexOf(';');
176
+ const kind = trimSpTab(i < 0 ? s : s.slice(0, i));
177
+ if (kind.toLowerCase() !== 'form-data')
178
+ bad('disposition');
179
+ const params = new Map();
180
+ let pos = i < 0 ? s.length : i;
181
+ const n = s.length;
182
+ while (pos < n) {
183
+ while (pos < n && (s[pos] === ' ' || s[pos] === '\t' || s[pos] === ';'))
184
+ pos++;
185
+ if (pos >= n)
186
+ break;
187
+ const start = pos;
188
+ while (pos < n && s[pos] !== '=' && s[pos] !== ';')
189
+ pos++;
190
+ const name = trimSpTab(s.slice(start, pos)).toLowerCase();
191
+ if (pos >= n || s[pos] === ';') {
192
+ if (!params.has(name))
193
+ params.set(name, '');
194
+ continue;
195
+ }
196
+ pos++; // '='
197
+ while (pos < n && (s[pos] === ' ' || s[pos] === '\t'))
198
+ pos++;
199
+ let val;
200
+ if (pos < n && s[pos] === '"') {
201
+ pos++;
202
+ let buf = '';
203
+ for (;;) {
204
+ if (pos >= n)
205
+ bad('unterminated quoted string');
206
+ const c = s[pos];
207
+ if (c === '\\' && pos + 1 < n) {
208
+ buf += s[pos + 1];
209
+ pos += 2;
210
+ continue;
211
+ }
212
+ if (c === '"') {
213
+ pos++;
214
+ break;
215
+ }
216
+ buf += c;
217
+ pos++;
218
+ }
219
+ val = buf;
220
+ while (pos < n && (s[pos] === ' ' || s[pos] === '\t'))
221
+ pos++;
222
+ if (pos < n && s[pos] !== ';')
223
+ bad('garbage after quoted string');
224
+ }
225
+ else {
226
+ const start2 = pos;
227
+ while (pos < n && s[pos] !== ';')
228
+ pos++;
229
+ val = trimSpTab(s.slice(start2, pos));
230
+ }
231
+ if (!params.has(name))
232
+ params.set(name, val);
233
+ }
234
+ return params;
235
+ }
236
+ function trimSpTabBytes(b) {
237
+ let a = 0;
238
+ let e = b.length;
239
+ while (a < e && (b[a] === 0x20 || b[a] === 0x09))
240
+ a++;
241
+ while (e > a && (b[e - 1] === 0x20 || b[e - 1] === 0x09))
242
+ e--;
243
+ return b.subarray(a, e);
244
+ }
245
+ /** 22.3 points 1-5: the parts of a multipart body. Throws InvalidMultipart. */
246
+ function parseMultipart(data, boundary) {
247
+ if (boundary === undefined || !BOUNDARY_RE.test(boundary))
248
+ bad('boundary');
249
+ const delim = Buffer.from('--' + boundary, 'latin1');
250
+ const crlfDelim = Buffer.concat([CRLF, delim]);
251
+ let pos;
252
+ if (startsWith(data, delim))
253
+ pos = 0;
254
+ else {
255
+ const i = data.indexOf(crlfDelim);
256
+ if (i < 0)
257
+ bad('no delimiter');
258
+ pos = i + 2;
259
+ }
260
+ const parts = [];
261
+ for (;;) {
262
+ pos += delim.length;
263
+ if (data[pos] === 0x2d && data[pos + 1] === 0x2d)
264
+ break;
265
+ while (pos < data.length && (data[pos] === 0x20 || data[pos] === 0x09))
266
+ pos++;
267
+ if (!(data[pos] === 0x0d && data[pos + 1] === 0x0a))
268
+ bad('delimiter line');
269
+ pos += 2;
270
+ const nxt = data.indexOf(crlfDelim, pos);
271
+ if (nxt < 0)
272
+ bad('no close delimiter');
273
+ parts.push(data.subarray(pos, nxt));
274
+ pos = nxt + 2;
275
+ }
276
+ const out = [];
277
+ for (const p of parts) {
278
+ let head;
279
+ let content;
280
+ if (p[0] === 0x0d && p[1] === 0x0a) {
281
+ head = p.subarray(0, 0);
282
+ content = p.subarray(2);
283
+ }
284
+ else {
285
+ const j = p.indexOf(CRLFCRLF);
286
+ if (j < 0)
287
+ bad('part headers');
288
+ head = p.subarray(0, j);
289
+ content = p.subarray(j + 4);
290
+ }
291
+ const headers = new Map();
292
+ if (head.length > 0) {
293
+ let start = 0;
294
+ for (;;) {
295
+ const e = head.indexOf(CRLF, start);
296
+ const line = head.subarray(start, e < 0 ? head.length : e);
297
+ const k = line.indexOf(0x3a);
298
+ if (k <= 0)
299
+ bad('part header line');
300
+ const nm = line.subarray(0, k).toString('latin1');
301
+ if (!PART_HEADER_NAME_RE.test(nm))
302
+ bad('part header line');
303
+ const lname = nm.toLowerCase();
304
+ if (!headers.has(lname))
305
+ headers.set(lname, trimSpTabBytes(line.subarray(k + 1)));
306
+ if (e < 0)
307
+ break;
308
+ start = e + 2;
309
+ }
310
+ }
311
+ const cd = headers.get('content-disposition');
312
+ if (cd === undefined)
313
+ bad('no content-disposition');
314
+ const cds = (0, utf8_js_1.decodeUtf8)(cd);
315
+ if (cds === null)
316
+ bad('content-disposition not UTF-8');
317
+ const params = disposition(cds);
318
+ const name = params.get('name');
319
+ if (name === undefined)
320
+ bad('no name');
321
+ const isFile = params.has('filename') || params.has('filename*');
322
+ let ct = null;
323
+ const ctb = headers.get('content-type');
324
+ if (ctb !== undefined) {
325
+ const cts = (0, utf8_js_1.decodeUtf8)(ctb);
326
+ if (cts !== null)
327
+ ct = trimSpTab(cts.split(';', 1)[0]).toLowerCase();
328
+ }
329
+ out.push({ name: name, isFile, content, ct });
330
+ }
331
+ return out;
332
+ }
333
+ /** 22.3: drop:file:<kind>:<class> of a file part. */
334
+ function fileToken(content, ct) {
335
+ const kind = magicKind(content) ?? FILE_TYPES.get(ct ?? '') ?? 'bin';
336
+ return 'drop:file:' + kind + ':' + docClass(content.length);
337
+ }
338
+ // ------------------------------------------------------------------ XML (22.2)
339
+ exports.NS_XML = 'http://www.w3.org/XML/1998/namespace';
340
+ exports.SOAP11 = 'http://schemas.xmlsoap.org/soap/envelope/';
341
+ exports.SOAP12 = 'http://www.w3.org/2003/05/soap-envelope';
342
+ exports.XML_MAX_NESTING = 256;
343
+ const NS_MAX_BYTES = 512;
344
+ class InvalidXml extends Error {
345
+ }
346
+ exports.InvalidXml = InvalidXml;
347
+ const xbad = (why) => {
348
+ throw new InvalidXml(why);
349
+ };
350
+ function nameStart(c) {
351
+ return (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) || c === 0x5f || c >= 0x80;
352
+ }
353
+ function nameChar(c) {
354
+ return nameStart(c) || (c >= 0x30 && c <= 0x39) || c === 0x2e || c === 0x2d || c === 0x3a;
355
+ }
356
+ function xmlChar(cp) {
357
+ return cp === 0x9 || cp === 0xa || cp === 0xd || (cp >= 0x20 && cp <= 0xd7ff) || (cp >= 0xe000 && cp <= 0xfffd) || (cp >= 0x10000 && cp <= 0x10ffff);
358
+ }
359
+ function isWs(c) {
360
+ return c === 0x20 || c === 0x09 || c === 0x0a;
361
+ }
362
+ const ENTITIES = new Map([['lt', '<'], ['gt', '>'], ['amp', '&'], ['apos', "'"], ['quot', '"']]);
363
+ const HEX_DIGITS = /^[0-9a-fA-F]+$/;
364
+ const DEC_DIGITS = /^[0-9]+$/;
365
+ /** Character and predefined entity references; attribute values turn tab and newline into spaces. */
366
+ function refs(raw, attr) {
367
+ if (raw.indexOf('&') < 0 && !(attr && /[\t\n]/.test(raw)))
368
+ return raw;
369
+ let out = '';
370
+ let i = 0;
371
+ const n = raw.length;
372
+ while (i < n) {
373
+ const c = raw[i];
374
+ if (c === '&') {
375
+ const j = raw.indexOf(';', i + 1);
376
+ if (j < 0)
377
+ xbad('unterminated reference');
378
+ const ent = raw.slice(i + 1, j);
379
+ const named = ENTITIES.get(ent);
380
+ if (named !== undefined)
381
+ out += named;
382
+ else {
383
+ let digits;
384
+ let radix;
385
+ if (ent.startsWith('#x') && ent.length > 2 && HEX_DIGITS.test(ent.slice(2))) {
386
+ digits = ent.slice(2);
387
+ radix = 16;
388
+ }
389
+ else if (ent.startsWith('#') && ent.length > 1 && DEC_DIGITS.test(ent.slice(1))) {
390
+ digits = ent.slice(1);
391
+ radix = 10;
392
+ }
393
+ else {
394
+ return xbad('unknown entity');
395
+ }
396
+ let cp = 0;
397
+ for (const d of digits) {
398
+ cp = cp * radix + parseInt(d, 16);
399
+ if (cp > 0x10ffff)
400
+ xbad('reference out of range');
401
+ }
402
+ if (!xmlChar(cp))
403
+ xbad('reference to a forbidden character');
404
+ out += String.fromCodePoint(cp);
405
+ }
406
+ i = j + 1;
407
+ continue;
408
+ }
409
+ out += attr && (c === '\t' || c === '\n') ? ' ' : c;
410
+ i++;
411
+ }
412
+ return out;
413
+ }
414
+ function splitQname(q) {
415
+ const i = q.indexOf(':');
416
+ if (i < 0)
417
+ return [null, q];
418
+ const p = q.slice(0, i);
419
+ const l = q.slice(i + 1);
420
+ if (!p || !l || l.includes(':'))
421
+ xbad('qualified name');
422
+ return [p, l];
423
+ }
424
+ /** 22.2 points 1-6 -> root element. Throws InvalidXml for anything outside the accepted subset. */
425
+ function parseXml(input) {
426
+ let data = input;
427
+ if (data.length >= 3 && data[0] === 0xef && data[1] === 0xbb && data[2] === 0xbf)
428
+ data = data.subarray(3);
429
+ const decoded = (0, utf8_js_1.decodeUtf8)(data);
430
+ if (decoded === null)
431
+ xbad('not UTF-8');
432
+ let s = decoded;
433
+ for (let i = 0; i < s.length; i++) {
434
+ const c = s.charCodeAt(i);
435
+ // Valid UTF-8 never yields lone surrogates: only pairs, which are allowed.
436
+ if ((c < 0x20 && c !== 9 && c !== 10 && c !== 13) || c === 0xfffe || c === 0xffff)
437
+ xbad('forbidden character');
438
+ }
439
+ if (s.indexOf('\r') >= 0)
440
+ s = s.replace(/\r\n?/g, '\n');
441
+ const n = s.length;
442
+ let pos = 0;
443
+ const readName = (p) => {
444
+ if (p >= n || !nameStart(s.charCodeAt(p)))
445
+ xbad('name');
446
+ let q = p + 1;
447
+ while (q < n && nameChar(s.charCodeAt(q)))
448
+ q++;
449
+ return [s.slice(p, q), q];
450
+ };
451
+ const skipWs = (p) => {
452
+ while (p < n && isWs(s.charCodeAt(p)))
453
+ p++;
454
+ return p;
455
+ };
456
+ /** Comment or processing instruction at p -> its end, or -1 if none starts there. */
457
+ const misc = (p) => {
458
+ if (s.startsWith('<!--', p)) {
459
+ const e = s.indexOf('-->', p + 4);
460
+ if (e < 0)
461
+ xbad('unterminated comment');
462
+ return e + 3;
463
+ }
464
+ if (s.startsWith('<?', p)) {
465
+ const e = s.indexOf('?>', p + 2);
466
+ if (e < 0)
467
+ xbad('unterminated processing instruction');
468
+ return e + 2;
469
+ }
470
+ return -1;
471
+ };
472
+ const startTag = (p0, parentNs) => {
473
+ let [qname, p] = readName(p0 + 1);
474
+ const rawAttrs = [];
475
+ const seen = new Set();
476
+ let empty = false;
477
+ for (;;) {
478
+ let q = skipWs(p);
479
+ if (s.startsWith('/>', q)) {
480
+ p = q + 2;
481
+ empty = true;
482
+ break;
483
+ }
484
+ if (s.startsWith('>', q)) {
485
+ p = q + 1;
486
+ break;
487
+ }
488
+ if (q === p)
489
+ xbad('attribute without space');
490
+ let aname;
491
+ [aname, q] = readName(q);
492
+ q = skipWs(q);
493
+ if (q >= n || s[q] !== '=')
494
+ xbad('attribute without =');
495
+ q = skipWs(q + 1);
496
+ if (q >= n || (s[q] !== '"' && s[q] !== "'"))
497
+ xbad('attribute value');
498
+ const e = s.indexOf(s[q], q + 1);
499
+ if (e < 0)
500
+ xbad('unterminated attribute value');
501
+ const raw = s.slice(q + 1, e);
502
+ if (raw.includes('<'))
503
+ xbad('< in attribute value');
504
+ if (seen.has(aname))
505
+ xbad('duplicate attribute');
506
+ seen.add(aname);
507
+ rawAttrs.push([aname, refs(raw, true)]);
508
+ p = e + 1;
509
+ }
510
+ let nsmap = parentNs;
511
+ for (const [aname, v] of rawAttrs) {
512
+ let pre = null;
513
+ if (aname === 'xmlns')
514
+ pre = '';
515
+ else if (aname.startsWith('xmlns:')) {
516
+ pre = aname.slice(6);
517
+ if (!pre || pre.includes(':'))
518
+ xbad('namespace declaration');
519
+ }
520
+ if (pre === null)
521
+ continue;
522
+ if (nsmap === parentNs)
523
+ nsmap = new Map(parentNs);
524
+ nsmap.set(pre, v);
525
+ }
526
+ const [prefix, local] = splitQname(qname);
527
+ let ns;
528
+ if (prefix === null)
529
+ ns = nsmap.get('') ?? '';
530
+ else if (prefix === 'xml')
531
+ ns = exports.NS_XML;
532
+ else if (nsmap.has(prefix))
533
+ ns = nsmap.get(prefix);
534
+ else
535
+ return xbad('undeclared prefix');
536
+ const attrs = [];
537
+ for (const [aname, v] of rawAttrs) {
538
+ if (aname === 'xmlns' || aname.startsWith('xmlns:'))
539
+ continue;
540
+ const [ap, al] = splitQname(aname);
541
+ if (ap !== null && ap !== 'xml' && !nsmap.has(ap))
542
+ xbad('undeclared attribute prefix');
543
+ attrs.push([al, v]);
544
+ }
545
+ return { qname, el: { local, ns, attrs, children: [], text: [] }, nsmap, empty, pos: p };
546
+ };
547
+ // prolog
548
+ for (;;) {
549
+ pos = skipWs(pos);
550
+ if (pos >= n)
551
+ xbad('no root element');
552
+ const e = misc(pos);
553
+ if (e >= 0) {
554
+ pos = e;
555
+ continue;
556
+ }
557
+ if (s.startsWith('<!', pos) || s[pos] !== '<')
558
+ xbad('prolog');
559
+ break;
560
+ }
561
+ const stack = [];
562
+ const first = startTag(pos, new Map());
563
+ const root = first.el;
564
+ pos = first.pos;
565
+ if (!first.empty)
566
+ stack.push({ qname: first.qname, el: first.el, nsmap: first.nsmap });
567
+ while (stack.length > 0) {
568
+ if (pos >= n)
569
+ xbad('unclosed element');
570
+ const top = stack[stack.length - 1];
571
+ if (s.charCodeAt(pos) !== 0x3c) {
572
+ const e = s.indexOf('<', pos);
573
+ if (e < 0)
574
+ xbad('unclosed element');
575
+ top.el.text.push(refs(s.slice(pos, e), false));
576
+ pos = e;
577
+ continue;
578
+ }
579
+ if (s.startsWith('<![CDATA[', pos)) {
580
+ const e = s.indexOf(']]>', pos + 9);
581
+ if (e < 0)
582
+ xbad('unterminated CDATA');
583
+ top.el.text.push(s.slice(pos + 9, e));
584
+ pos = e + 3;
585
+ continue;
586
+ }
587
+ const e = misc(pos);
588
+ if (e >= 0) {
589
+ pos = e;
590
+ continue;
591
+ }
592
+ if (s.startsWith('<!', pos))
593
+ xbad('declaration in content');
594
+ if (s.startsWith('</', pos)) {
595
+ const [name, q0] = readName(pos + 2);
596
+ const q = skipWs(q0);
597
+ if (q >= n || s[q] !== '>' || name !== top.qname)
598
+ xbad('end tag');
599
+ stack.pop();
600
+ pos = q + 1;
601
+ continue;
602
+ }
603
+ const t = startTag(pos, top.nsmap);
604
+ top.el.children.push(t.el);
605
+ pos = t.pos;
606
+ if (!t.empty) {
607
+ if (stack.length >= exports.XML_MAX_NESTING)
608
+ xbad('nesting');
609
+ stack.push({ qname: t.qname, el: t.el, nsmap: t.nsmap });
610
+ }
611
+ }
612
+ // epilogue
613
+ for (;;) {
614
+ pos = skipWs(pos);
615
+ if (pos >= n)
616
+ break;
617
+ const e = misc(pos);
618
+ if (e < 0)
619
+ xbad('content after the root element');
620
+ pos = e;
621
+ }
622
+ return root;
623
+ }
624
+ function hasNonSpace(t) {
625
+ for (let i = 0; i < t.length; i++) {
626
+ const c = t.charCodeAt(i);
627
+ if (c !== 0x20 && c !== 0x09 && c !== 0x0a && c !== 0x0d)
628
+ return true;
629
+ }
630
+ return false;
631
+ }
632
+ function xmlValue(el) {
633
+ if (el.attrs.length === 0 && el.children.length === 0)
634
+ return el.text.join('');
635
+ let members = [];
636
+ const index = new Map();
637
+ for (const [local, v] of el.attrs) {
638
+ // Same local name twice: the last wins, at its own position.
639
+ const k = '@' + local;
640
+ const prev = index.get(k);
641
+ if (prev !== undefined)
642
+ members[prev] = null;
643
+ index.set(k, members.length);
644
+ members.push([k, v]);
645
+ }
646
+ members = members.filter((m) => m !== null);
647
+ const groups = new Map();
648
+ for (const c of el.children) {
649
+ let g = groups.get(c.local);
650
+ if (!g) {
651
+ g = [];
652
+ groups.set(c.local, g);
653
+ }
654
+ g.push(xmlValue(c));
655
+ }
656
+ for (const [k, vs] of groups)
657
+ members.push([k, vs.length === 1 ? vs[0] : vs]);
658
+ const text = el.text.join('');
659
+ if (hasNonSpace(text))
660
+ members.push(['#text', text]);
661
+ return { members: members };
662
+ }
663
+ const NS_RUN_RE = /[A-Za-z0-9_-]+/g;
664
+ const NS_BAD_CHAR = /[^!-~]|[@{}]/;
665
+ /** 22.2 point 9: 'ns:' + pattern of a namespace URI. */
666
+ function nsPattern(uri) {
667
+ if (uri === '')
668
+ return 'ns:{none}';
669
+ if (Buffer.byteLength(uri, 'utf8') > NS_MAX_BYTES || NS_BAD_CHAR.test(uri))
670
+ return 'ns:{*}';
671
+ return 'ns:' + uri.replace(NS_RUN_RE, (t) => (t.length > 64 || /[0-9]{6}/.test(t) || /[0-9A-Fa-f]{12}/.test(t) ? '{*}' : t));
672
+ }
673
+ /** 22.2 points 9-10: xmlns entries [(path, token)] in document order (no limit) and fmt. */
674
+ function xmlNamespaces(root, literal, maxDepth) {
675
+ const entries = [];
676
+ const seen = new Set();
677
+ const visit = (el, path, parentNs, depth) => {
678
+ if (depth > maxDepth)
679
+ return;
680
+ if (el.ns !== parentNs && !seen.has(path)) {
681
+ seen.add(path);
682
+ entries.push([path, nsPattern(el.ns)]);
683
+ }
684
+ const counts = new Map();
685
+ for (const c of el.children)
686
+ counts.set(c.local, (counts.get(c.local) ?? 0) + 1);
687
+ for (const c of el.children) {
688
+ const p = path + (literal(c.local) ? '.' + c.local : '.*') + (counts.get(c.local) > 1 ? '[*]' : '');
689
+ visit(c, p, el.ns, depth + 1);
690
+ }
691
+ };
692
+ visit(root, '$' + (literal(root.local) ? '.' + root.local : '.*'), '', 1);
693
+ let fmt = 'xml';
694
+ if (root.local === 'Envelope' && root.ns === exports.SOAP11)
695
+ fmt = 'soap11';
696
+ else if (root.local === 'Envelope' && root.ns === exports.SOAP12)
697
+ fmt = 'soap12';
698
+ return { entries, fmt };
699
+ }
700
+ /** 22.2 point 11: the operation name of a SOAPAction value, or null when empty. */
701
+ function soapActionName(raw, literal) {
702
+ let s = trimSpTab(raw);
703
+ if (s.length >= 2 && s.startsWith('"') && s.endsWith('"'))
704
+ s = s.slice(1, -1);
705
+ if (s === '')
706
+ return null;
707
+ const cut = Math.max(s.lastIndexOf('/'), s.lastIndexOf('#'), s.lastIndexOf(':'));
708
+ const name = s.slice(cut + 1);
709
+ return literal(name) ? name : '{*}';
710
+ }