1(function (root, factory) { 2 var api = factory(); 3 if (typeof module === 'object' && module.exports) module.exports = api; 4 if (typeof window !== 'undefined') window.QuickRouterDebug = api; 5 else if (typeof module !== 'object') root.QuickRouterDebug = api; 6})(typeof globalThis !== 'undefined' ? globalThis : this, function () { 7 'use strict'; 8 function buildRequest(input) { 9 var method = (input.method || 'GET').toUpperCase(); 10 var address = input.url.replace(/\{([^}]+)\}/g, function (_, name) { 11 var value = input.path && input.path[name]; 12 if (value === undefined || value === '') throw new Error('请填åè·¯å¾åæ° ' + name); 13 return encodeURIComponent(value); 14 }); 15 var url = new URL(address); 16 if (!['https:', 'http:'].includes(url.protocol)) throw new Error('请æ±å°å须为 HTTP æ HTTPS'); 17 var queryNames = new Set(); 18 (input.query || []).forEach(function (pair) { 19 if (!pair.name || pair.enabled === false) return; 20 if (pair.required && !String(pair.value || '').trim()) throw new Error('请填å Query åæ° ' + pair.name); 21 if (!queryNames.has(pair.name)) { url.searchParams.delete(pair.name); queryNames.add(pair.name); } 22 url.searchParams.append(pair.name, pair.value); 23 }); 24 var headers = new Headers(input.headers || {}); 25 var options = { method: method, headers: headers, signal: input.signal }; 26 if (!['GET', 'HEAD'].includes(method)) { 27 if (input.bodyType === 'multipart') { 28 headers.delete('Content-Type'); 29 var form = new FormData(); 30 (input.fields || []).forEach(function (field) { if (field.name && field.enabled !== false) form.append(field.name, field.value); }); 31 (input.files || []).forEach(function (field) { 32 if (field.enabled === false) return; 33 if ((field.values || []).length && !field.name) throw new Error('请填åæä»¶å段å'); 34 (field.values || []).forEach(function (file) { form.append(field.name, file, file.name || 'upload.bin'); }); 35 if (field.required && !(field.values || []).length) throw new Error('è¯·éæ©æä»¶ ' + field.name); 36 }); 37 options.body = form; 38 } else if (input.bodyType === 'json' && String(input.body || '').trim()) { 39 JSON.parse(input.body); 40 headers.set('Content-Type', 'application/json'); 41 options.body = input.body; 42 } 43 } 44 return { url: url.toString(), options: options }; 45 } 46 async function readSSE(response, onEvent, signal) { 47 if (!response.body) throw new Error('ååºæ²¡æå¯è¯»åçæµ'); 48 var reader = response.body.getReader(); 49 var decoder = new TextDecoder(); 50 var buffer = '', event = { event: 'message', data: [] }, doneMarker = false;
51 function line(value) { 52 if (!value) { 53 if (event.data.length) { 54 var data = event.data.join('\n'); 55 onEvent({ event: event.event, data: data }); 56 if (data === '[DONE]') doneMarker = true; 57 } 58 event = { event: 'message', data: [] }; return; 59 } 60 if (value[0] === ':') return; 61 var colon = value.indexOf(':'); 62 var key = colon < 0 ? value : value.slice(0, colon); 63 var data = colon < 0 ? '' : value.slice(colon + 1).replace(/^ /, ''); 64 if (key === 'event') event.event = data; 65 if (key === 'data') event.data.push(data); 66 } 67 function consume(final) { 68 var match; 69 while (!doneMarker && (match = /\r\n|\r(?!$)|\n/.exec(buffer))) { 70 line(buffer.slice(0, match.index)); buffer = buffer.slice(match.index + match[0].length); 71 } 72 if (final && !doneMarker) { if (buffer) line(buffer.replace(/\r$/, '')); buffer = ''; line(''); } 73 } 74 try { 75 while (true) { 76 if (signal && signal.aborted) throw new DOMException('请æ±å·²åæ¶', 'AbortError'); 77 var part = await reader.read(); 78 buffer += decoder.decode(part.value || new Uint8Array(), { stream: !part.done }); 79 consume(part.done); 80 if (doneMarker) { await reader.cancel(); break; } 81 if (part.done) break; 82 } 83 } finally { reader.releaseLock(); } 84 } 85 function filename(response, fallback) { 86 var disposition = response.headers.get('content-disposition') || ''; 87 var encoded = disposition.match(/filename\*=UTF-8''([^;]+)/i), plain = disposition.match(/filename="([^"]+)"|filename=([^;]+)/i); 88 var name = fallback || 'result.bin'; 89 if (encoded) { try { name = decodeURIComponent(encoded[1]); } catch (_) {} } 90 else if (plain) name = plain[1] || plain[2]; 91 return name.replace(/[\/\\\x00-\x1f]/g, '_').trim() || 'result.bin'; 92 } 93 async function readResponse(response, settings) { 94 settings = settings || {}; 95 var type = (response.headers.get('content-type') || '').toLowerCase(); 96 if (!response.ok) return { kind: 'text', text: await response.text() }; 97 if (type.includes('text/event-stream')) { 98 await readSSE(response, settings.onEvent || function () {}, settings.signal); 99 return { kind: 'stream' }; 100 } 101 if (settings.download || /^(video|audio|image)\//.test(type) || type.includes('application/octet-stream') || /attachment/i.test(response.headers.get('content-disposition') || '')) { 102 return { kind: 'file', blob: await response.blob(), filename: filename(response, settings.fallbackName) }; 103 } 104 return { kind: 'text', text: await response.text() }; 105 } 106 return { buildRequest: buildRequest, readSSE: readSSE, readResponse: readResponse, filename: filename }; 107});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.