PageSourceSearch

https://doc.quickrouter.ai/js/api-debug-core.js?v=80fcf033af262494

js quickrouter.ai collected 2026-10-05 12:28:54 UTC 5,497 bytes, 107 lines download raw bytes

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.