deepspider 0.1.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/.claude/agents/check.md +122 -0
- package/.claude/agents/debug.md +106 -0
- package/.claude/agents/dispatch.md +214 -0
- package/.claude/agents/implement.md +96 -0
- package/.claude/agents/plan.md +396 -0
- package/.claude/agents/research.md +120 -0
- package/.claude/commands/evolve/merge.md +80 -0
- package/.claude/commands/trellis/before-backend-dev.md +13 -0
- package/.claude/commands/trellis/before-frontend-dev.md +13 -0
- package/.claude/commands/trellis/break-loop.md +107 -0
- package/.claude/commands/trellis/check-backend.md +13 -0
- package/.claude/commands/trellis/check-cross-layer.md +153 -0
- package/.claude/commands/trellis/check-frontend.md +13 -0
- package/.claude/commands/trellis/create-command.md +154 -0
- package/.claude/commands/trellis/finish-work.md +129 -0
- package/.claude/commands/trellis/integrate-skill.md +219 -0
- package/.claude/commands/trellis/onboard.md +358 -0
- package/.claude/commands/trellis/parallel.md +193 -0
- package/.claude/commands/trellis/record-session.md +62 -0
- package/.claude/commands/trellis/start.md +280 -0
- package/.claude/commands/trellis/update-spec.md +213 -0
- package/.claude/hooks/inject-subagent-context.py +758 -0
- package/.claude/hooks/ralph-loop.py +374 -0
- package/.claude/hooks/session-start.py +126 -0
- package/.claude/settings.json +41 -0
- package/.claude/skills/deepagents-guide/SKILL.md +428 -0
- package/.cursor/commands/trellis-before-backend-dev.md +13 -0
- package/.cursor/commands/trellis-before-frontend-dev.md +13 -0
- package/.cursor/commands/trellis-break-loop.md +107 -0
- package/.cursor/commands/trellis-check-backend.md +13 -0
- package/.cursor/commands/trellis-check-cross-layer.md +153 -0
- package/.cursor/commands/trellis-check-frontend.md +13 -0
- package/.cursor/commands/trellis-create-command.md +154 -0
- package/.cursor/commands/trellis-finish-work.md +129 -0
- package/.cursor/commands/trellis-integrate-skill.md +219 -0
- package/.cursor/commands/trellis-onboard.md +358 -0
- package/.cursor/commands/trellis-record-session.md +62 -0
- package/.cursor/commands/trellis-start.md +156 -0
- package/.cursor/commands/trellis-update-spec.md +213 -0
- package/.env.example +11 -0
- package/.husky/pre-commit +1 -0
- package/.mcp.json +8 -0
- package/.trellis/.template-hashes.json +65 -0
- package/.trellis/.version +1 -0
- package/.trellis/scripts/add-session.sh +384 -0
- package/.trellis/scripts/common/developer.sh +129 -0
- package/.trellis/scripts/common/git-context.sh +263 -0
- package/.trellis/scripts/common/paths.sh +208 -0
- package/.trellis/scripts/common/phase.sh +150 -0
- package/.trellis/scripts/common/registry.sh +247 -0
- package/.trellis/scripts/common/task-queue.sh +142 -0
- package/.trellis/scripts/common/task-utils.sh +151 -0
- package/.trellis/scripts/common/worktree.sh +128 -0
- package/.trellis/scripts/create-bootstrap.sh +299 -0
- package/.trellis/scripts/get-context.sh +7 -0
- package/.trellis/scripts/get-developer.sh +15 -0
- package/.trellis/scripts/init-developer.sh +34 -0
- package/.trellis/scripts/multi-agent/cleanup.sh +396 -0
- package/.trellis/scripts/multi-agent/create-pr.sh +241 -0
- package/.trellis/scripts/multi-agent/plan.sh +207 -0
- package/.trellis/scripts/multi-agent/start.sh +310 -0
- package/.trellis/scripts/multi-agent/status.sh +828 -0
- package/.trellis/scripts/task.sh +1118 -0
- package/.trellis/spec/backend/deepagents-guide.md +337 -0
- package/.trellis/spec/backend/directory-structure.md +126 -0
- package/.trellis/spec/backend/examples/skills/deepagents-guide/README.md +11 -0
- package/.trellis/spec/backend/examples/skills/deepagents-guide/agent.js.template +20 -0
- package/.trellis/spec/backend/examples/skills/deepagents-guide/skills-config.js.template +13 -0
- package/.trellis/spec/backend/examples/skills/deepagents-guide/subagent.js.template +19 -0
- package/.trellis/spec/backend/hook-guidelines.md +178 -0
- package/.trellis/spec/backend/index.md +36 -0
- package/.trellis/spec/backend/quality-guidelines.md +201 -0
- package/.trellis/spec/backend/state-management.md +76 -0
- package/.trellis/spec/backend/tool-guidelines.md +144 -0
- package/.trellis/spec/backend/type-safety.md +71 -0
- package/.trellis/spec/guides/code-reuse-thinking-guide.md +92 -0
- package/.trellis/spec/guides/cross-layer-thinking-guide.md +94 -0
- package/.trellis/spec/guides/index.md +79 -0
- package/.trellis/tasks/archive/02-02-evolving-skills/prd.md +61 -0
- package/.trellis/tasks/archive/02-02-evolving-skills/task.json +29 -0
- package/.trellis/tasks/archive/2026-02/00-bootstrap-guidelines/prd.md +86 -0
- package/.trellis/tasks/archive/2026-02/00-bootstrap-guidelines/task.json +27 -0
- package/.trellis/tasks/archive/2026-02/02-02-skills-system/check.jsonl +3 -0
- package/.trellis/tasks/archive/2026-02/02-02-skills-system/debug.jsonl +2 -0
- package/.trellis/tasks/archive/2026-02/02-02-skills-system/implement.jsonl +5 -0
- package/.trellis/tasks/archive/2026-02/02-02-skills-system/prd.md +33 -0
- package/.trellis/tasks/archive/2026-02/02-02-skills-system/task.json +41 -0
- package/.trellis/workflow.md +407 -0
- package/.trellis/workspace/index.md +123 -0
- package/.trellis/workspace/pony/index.md +40 -0
- package/.trellis/workspace/pony/journal-1.md +7 -0
- package/.trellis/worktree.yaml +47 -0
- package/AGENTS.md +18 -0
- package/CLAUDE.md +292 -0
- package/README.md +134 -0
- package/agents/deepspider.md +142 -0
- package/docs/DEBUG.md +42 -0
- package/docs/GUIDE.md +334 -0
- package/docs/PROMPT.md +60 -0
- package/docs/USAGE.md +226 -0
- package/eslint.config.js +51 -0
- package/package.json +78 -0
- package/requirements-crypto.txt +14 -0
- package/src/agent/index.js +97 -0
- package/src/agent/logger.js +164 -0
- package/src/agent/middleware/filterTools.js +64 -0
- package/src/agent/middleware/report.js +79 -0
- package/src/agent/prompts/system.js +315 -0
- package/src/agent/run.js +575 -0
- package/src/agent/skills/anti-detect/SKILL.md +28 -0
- package/src/agent/skills/anti-detect/evolved.md +12 -0
- package/src/agent/skills/captcha/SKILL.md +37 -0
- package/src/agent/skills/captcha/evolved.md +12 -0
- package/src/agent/skills/config.js +30 -0
- package/src/agent/skills/crawler/SKILL.md +9 -0
- package/src/agent/skills/crawler/evolved.md +16 -0
- package/src/agent/skills/dynamic-analysis/SKILL.md +91 -0
- package/src/agent/skills/dynamic-analysis/evolved.md +12 -0
- package/src/agent/skills/env/SKILL.md +72 -0
- package/src/agent/skills/env/evolved.md +12 -0
- package/src/agent/skills/evolve.js +79 -0
- package/src/agent/skills/general/SKILL.md +12 -0
- package/src/agent/skills/general/evolved.md +12 -0
- package/src/agent/skills/js2python/SKILL.md +30 -0
- package/src/agent/skills/js2python/evolved.md +13 -0
- package/src/agent/skills/report/SKILL.md +21 -0
- package/src/agent/skills/report/evolved.md +12 -0
- package/src/agent/skills/sandbox/SKILL.md +22 -0
- package/src/agent/skills/sandbox/evolved.md +16 -0
- package/src/agent/skills/static-analysis/SKILL.md +93 -0
- package/src/agent/skills/static-analysis/evolved.md +12 -0
- package/src/agent/skills/xpath/SKILL.md +119 -0
- package/src/agent/subagents/anti-detect.js +45 -0
- package/src/agent/subagents/captcha.js +51 -0
- package/src/agent/subagents/crawler.js +138 -0
- package/src/agent/subagents/dynamic.js +64 -0
- package/src/agent/subagents/env-agent.js +82 -0
- package/src/agent/subagents/index.js +37 -0
- package/src/agent/subagents/js2python.js +72 -0
- package/src/agent/subagents/sandbox.js +55 -0
- package/src/agent/subagents/static.js +66 -0
- package/src/agent/tools/analysis.js +135 -0
- package/src/agent/tools/analyzer.js +85 -0
- package/src/agent/tools/anti-detect.js +89 -0
- package/src/agent/tools/antidebug.js +64 -0
- package/src/agent/tools/async.js +43 -0
- package/src/agent/tools/browser.js +324 -0
- package/src/agent/tools/captcha.js +223 -0
- package/src/agent/tools/capture.js +179 -0
- package/src/agent/tools/correlate.js +303 -0
- package/src/agent/tools/crawler.js +116 -0
- package/src/agent/tools/cryptohook.js +80 -0
- package/src/agent/tools/debug.js +246 -0
- package/src/agent/tools/deobfuscator.js +90 -0
- package/src/agent/tools/env.js +83 -0
- package/src/agent/tools/envdump.js +92 -0
- package/src/agent/tools/evolve.js +164 -0
- package/src/agent/tools/extract.js +114 -0
- package/src/agent/tools/extractor.js +54 -0
- package/src/agent/tools/file.js +224 -0
- package/src/agent/tools/hook.js +84 -0
- package/src/agent/tools/hookManager.js +178 -0
- package/src/agent/tools/index.js +137 -0
- package/src/agent/tools/nodejs.js +101 -0
- package/src/agent/tools/patch.js +46 -0
- package/src/agent/tools/preprocess.js +71 -0
- package/src/agent/tools/profile.js +122 -0
- package/src/agent/tools/python.js +627 -0
- package/src/agent/tools/report.js +124 -0
- package/src/agent/tools/runtime.js +132 -0
- package/src/agent/tools/sandbox.js +79 -0
- package/src/agent/tools/store.js +73 -0
- package/src/agent/tools/trace.js +74 -0
- package/src/agent/tools/tracing.js +201 -0
- package/src/agent/tools/utils.js +51 -0
- package/src/agent/tools/verify.js +184 -0
- package/src/agent/tools/webcrack.js +109 -0
- package/src/analyzer/ASTAnalyzer.js +387 -0
- package/src/analyzer/CallStackAnalyzer.js +379 -0
- package/src/analyzer/Deobfuscator.js +289 -0
- package/src/analyzer/EncryptionAnalyzer.js +99 -0
- package/src/analyzer/index.js +22 -0
- package/src/browser/EnvBridge.js +186 -0
- package/src/browser/cdp.js +168 -0
- package/src/browser/client.js +197 -0
- package/src/browser/collector.js +444 -0
- package/src/browser/collectors/RequestCryptoLinker.js +109 -0
- package/src/browser/collectors/ResponseSearcher.js +107 -0
- package/src/browser/collectors/ScriptCollector.js +158 -0
- package/src/browser/collectors/index.js +26 -0
- package/src/browser/defaultHooks.js +932 -0
- package/src/browser/hooks/crypto.js +55 -0
- package/src/browser/hooks/index.js +64 -0
- package/src/browser/hooks/native.js +9 -0
- package/src/browser/hooks/network.js +33 -0
- package/src/browser/index.js +42 -0
- package/src/browser/interceptors/NetworkInterceptor.js +116 -0
- package/src/browser/interceptors/ScriptInterceptor.js +76 -0
- package/src/browser/interceptors/index.js +6 -0
- package/src/browser/ui/analysisPanel.js +1782 -0
- package/src/browser/ui/confirmDialog.js +158 -0
- package/src/browser/ui/panel.html +152 -0
- package/src/browser/ui/selector.js +170 -0
- package/src/config/index.js +5 -0
- package/src/config/paths.js +71 -0
- package/src/config/patterns/crypto.js +36 -0
- package/src/config/profiles/chrome.json +71 -0
- package/src/config/profiles/firefox.json +44 -0
- package/src/config/profiles/safari.json +38 -0
- package/src/core/EnvMonitor.js +200 -0
- package/src/core/PatchGenerator.js +278 -0
- package/src/core/Sandbox.js +181 -0
- package/src/env/AntiAntiDebug.js +111 -0
- package/src/env/AsyncHook.js +68 -0
- package/src/env/BrowserAPIList.js +265 -0
- package/src/env/CookieHook.js +48 -0
- package/src/env/CryptoHook.js +205 -0
- package/src/env/EnvCodeGenerator.js +157 -0
- package/src/env/EnvDumper.js +356 -0
- package/src/env/EnvExtractor.js +220 -0
- package/src/env/HookBase.js +618 -0
- package/src/env/NetworkHook.js +159 -0
- package/src/env/modules/bom/history.js +29 -0
- package/src/env/modules/bom/location.js +26 -0
- package/src/env/modules/bom/navigator.js +70 -0
- package/src/env/modules/bom/screen.js +26 -0
- package/src/env/modules/bom/storage.js +23 -0
- package/src/env/modules/dom/document.js +110 -0
- package/src/env/modules/dom/event.js +51 -0
- package/src/env/modules/index.js +34 -0
- package/src/env/modules/webapi/fetch.js +46 -0
- package/src/env/modules/webapi/url.js +47 -0
- package/src/env/modules/webapi/xhr.js +48 -0
- package/src/index.js +27 -0
- package/src/mcp/server.js +89 -0
- package/src/store/DataStore.js +708 -0
- package/src/store/Store.js +158 -0
- package/src/store/Validator.js +24 -0
- package/test/analyze.test.js +90 -0
- package/test/envdump.test.js +74 -0
- package/test/flow.test.js +90 -0
- package/test/hooks.test.js +138 -0
- package/test/plugin.test.js +35 -0
- package/test/refactor-full.test.js +30 -0
- package/test/refactor.test.js +21 -0
- package/test/samples/obfuscated.js +61 -0
- package/test/samples/original.js +66 -0
- package/test/samples/v10_eval_chain.js +52 -0
- package/test/samples/v11_bytecode_vm.js +81 -0
- package/test/samples/v12_polymorphic.js +69 -0
- package/test/samples/v1_ob_basic.js +98 -0
- package/test/samples/v2_ob_advanced.js +99 -0
- package/test/samples/v3_jjencode.js +77 -0
- package/test/samples/v4_aaencode.js +73 -0
- package/test/samples/v5_control_flow.js +86 -0
- package/test/samples/v6_string_encryption.js +71 -0
- package/test/samples/v7_jsvmp.js +83 -0
- package/test/samples/v8_anti_debug.js +79 -0
- package/test/samples/v9_proxy_trap.js +49 -0
- package/test/samples.test.js +96 -0
- package/test/webcrack.test.js +55 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSpider - 网络请求 Hook 模块
|
|
3
|
+
* 拦截 XHR 和 Fetch 请求/响应
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { HookBase } from './HookBase.js';
|
|
7
|
+
|
|
8
|
+
export class NetworkHook {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.logs = [];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 生成 XHR Hook 代码
|
|
15
|
+
*/
|
|
16
|
+
generateXHRHookCode(options = {}) {
|
|
17
|
+
const { captureBody = true, captureResponse = true } = options;
|
|
18
|
+
|
|
19
|
+
return HookBase.getBaseCode() + `
|
|
20
|
+
(function() {
|
|
21
|
+
const deepspider = window.__deepspider__;
|
|
22
|
+
if (!deepspider) return;
|
|
23
|
+
|
|
24
|
+
const OriginalXHR = XMLHttpRequest;
|
|
25
|
+
|
|
26
|
+
XMLHttpRequest = function() {
|
|
27
|
+
const xhr = new OriginalXHR();
|
|
28
|
+
const log = {
|
|
29
|
+
method: '',
|
|
30
|
+
url: '',
|
|
31
|
+
requestHeaders: {},
|
|
32
|
+
requestBody: null,
|
|
33
|
+
response: null,
|
|
34
|
+
status: 0
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Hook open
|
|
38
|
+
const originalOpen = xhr.open;
|
|
39
|
+
xhr.open = deepspider.native(function(method, url) {
|
|
40
|
+
log.method = method;
|
|
41
|
+
log.url = url;
|
|
42
|
+
return originalOpen.apply(xhr, arguments);
|
|
43
|
+
}, originalOpen);
|
|
44
|
+
|
|
45
|
+
// Hook setRequestHeader
|
|
46
|
+
const originalSetHeader = xhr.setRequestHeader;
|
|
47
|
+
xhr.setRequestHeader = deepspider.native(function(name, value) {
|
|
48
|
+
log.requestHeaders[name] = value;
|
|
49
|
+
return originalSetHeader.apply(xhr, arguments);
|
|
50
|
+
}, originalSetHeader);
|
|
51
|
+
|
|
52
|
+
// Hook send
|
|
53
|
+
const originalSend = xhr.send;
|
|
54
|
+
xhr.send = deepspider.native(function(body) {
|
|
55
|
+
// 开始请求上下文
|
|
56
|
+
log.requestId = deepspider.startRequest(log.url, log.method);
|
|
57
|
+
${captureBody ? `log.requestBody = body;` : ''}
|
|
58
|
+
deepspider.log('xhr', { action: 'send', ...log, body: body?.toString().slice(0, 200) });
|
|
59
|
+
return originalSend.apply(xhr, arguments);
|
|
60
|
+
}, originalSend);
|
|
61
|
+
|
|
62
|
+
${captureResponse ? `
|
|
63
|
+
xhr.addEventListener('load', function() {
|
|
64
|
+
log.status = xhr.status;
|
|
65
|
+
log.response = xhr.responseText?.slice(0, 500);
|
|
66
|
+
// 结束请求上下文,获取关联的加密调用
|
|
67
|
+
const ctx = deepspider.endRequest();
|
|
68
|
+
deepspider.log('xhr', {
|
|
69
|
+
action: 'response',
|
|
70
|
+
url: log.url,
|
|
71
|
+
status: log.status,
|
|
72
|
+
response: log.response?.slice(0, 100),
|
|
73
|
+
linkedCrypto: ctx?.cryptoCalls || []
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
` : ''}
|
|
77
|
+
|
|
78
|
+
return xhr;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
XMLHttpRequest.prototype = OriginalXHR.prototype;
|
|
82
|
+
console.log('[DeepSpider:xhr] XHR Hook 已启用');
|
|
83
|
+
})();
|
|
84
|
+
`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 生成 Fetch Hook 代码
|
|
89
|
+
*/
|
|
90
|
+
generateFetchHookCode(options = {}) {
|
|
91
|
+
const { captureBody = true, captureResponse = true } = options;
|
|
92
|
+
|
|
93
|
+
return HookBase.getBaseCode() + `
|
|
94
|
+
(function() {
|
|
95
|
+
const deepspider = window.__deepspider__;
|
|
96
|
+
if (!deepspider) return;
|
|
97
|
+
|
|
98
|
+
const OriginalFetch = fetch;
|
|
99
|
+
|
|
100
|
+
fetch = deepspider.native(async function(url, options = {}) {
|
|
101
|
+
const log = {
|
|
102
|
+
url: typeof url === 'string' ? url : url.url,
|
|
103
|
+
method: options.method || 'GET',
|
|
104
|
+
headers: options.headers || {},
|
|
105
|
+
body: null,
|
|
106
|
+
response: null,
|
|
107
|
+
status: 0
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// 开始请求上下文
|
|
111
|
+
log.requestId = deepspider.startRequest(log.url, log.method);
|
|
112
|
+
|
|
113
|
+
${captureBody ? `
|
|
114
|
+
if (options.body) {
|
|
115
|
+
log.body = typeof options.body === 'string' ? options.body.slice(0, 500) : '[FormData/Blob]';
|
|
116
|
+
}
|
|
117
|
+
` : ''}
|
|
118
|
+
|
|
119
|
+
deepspider.log('fetch', { action: 'request', ...log });
|
|
120
|
+
|
|
121
|
+
const response = await OriginalFetch.apply(this, arguments);
|
|
122
|
+
log.status = response.status;
|
|
123
|
+
|
|
124
|
+
${captureResponse ? `
|
|
125
|
+
try {
|
|
126
|
+
const cloned = response.clone();
|
|
127
|
+
const text = await cloned.text();
|
|
128
|
+
log.response = text.slice(0, 500);
|
|
129
|
+
// 结束请求上下文
|
|
130
|
+
const ctx = deepspider.endRequest();
|
|
131
|
+
deepspider.log('fetch', {
|
|
132
|
+
action: 'response',
|
|
133
|
+
url: log.url,
|
|
134
|
+
status: log.status,
|
|
135
|
+
response: log.response.slice(0, 100),
|
|
136
|
+
linkedCrypto: ctx?.cryptoCalls || []
|
|
137
|
+
});
|
|
138
|
+
} catch (e) {
|
|
139
|
+
deepspider.endRequest();
|
|
140
|
+
}
|
|
141
|
+
` : 'deepspider.endRequest();'}
|
|
142
|
+
|
|
143
|
+
return response;
|
|
144
|
+
}, OriginalFetch);
|
|
145
|
+
|
|
146
|
+
console.log('[DeepSpider:fetch] Fetch Hook 已启用');
|
|
147
|
+
})();
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 生成完整的网络 Hook 代码
|
|
153
|
+
*/
|
|
154
|
+
generateFullNetworkHookCode(options = {}) {
|
|
155
|
+
return this.generateXHRHookCode(options) + '\n' + this.generateFetchHookCode(options);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export default NetworkHook;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSpider - History 环境模块
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const historyCode = `
|
|
6
|
+
(function() {
|
|
7
|
+
const _history = [];
|
|
8
|
+
let _index = 0;
|
|
9
|
+
|
|
10
|
+
window.history = {
|
|
11
|
+
get length() { return _history.length || 1; },
|
|
12
|
+
get state() { return _history[_index]?.state || null; },
|
|
13
|
+
scrollRestoration: 'auto',
|
|
14
|
+
back: function() { if (_index > 0) _index--; },
|
|
15
|
+
forward: function() { if (_index < _history.length - 1) _index++; },
|
|
16
|
+
go: function(delta) { _index = Math.max(0, Math.min(_history.length - 1, _index + delta)); },
|
|
17
|
+
pushState: function(state, title, url) {
|
|
18
|
+
_history.splice(_index + 1);
|
|
19
|
+
_history.push({ state, title, url });
|
|
20
|
+
_index = _history.length - 1;
|
|
21
|
+
},
|
|
22
|
+
replaceState: function(state, title, url) {
|
|
23
|
+
_history[_index] = { state, title, url };
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
})();
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
export default historyCode;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSpider - Location 环境模块
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const locationCode = `
|
|
6
|
+
(function() {
|
|
7
|
+
const location = {
|
|
8
|
+
href: 'https://example.com/',
|
|
9
|
+
protocol: 'https:',
|
|
10
|
+
host: 'example.com',
|
|
11
|
+
hostname: 'example.com',
|
|
12
|
+
port: '',
|
|
13
|
+
pathname: '/',
|
|
14
|
+
search: '',
|
|
15
|
+
hash: '',
|
|
16
|
+
origin: 'https://example.com',
|
|
17
|
+
assign: function(url) { this.href = url; },
|
|
18
|
+
replace: function(url) { this.href = url; },
|
|
19
|
+
reload: function() {},
|
|
20
|
+
toString: function() { return this.href; }
|
|
21
|
+
};
|
|
22
|
+
window.location = location;
|
|
23
|
+
})();
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
export default locationCode;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSpider - Navigator 环境模块
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const navigatorCode = `
|
|
6
|
+
(function() {
|
|
7
|
+
const navigator = {
|
|
8
|
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36',
|
|
9
|
+
appCodeName: 'Mozilla',
|
|
10
|
+
appName: 'Netscape',
|
|
11
|
+
appVersion: '5.0 (Windows NT 10.0; Win64; x64)',
|
|
12
|
+
platform: 'Win32',
|
|
13
|
+
product: 'Gecko',
|
|
14
|
+
vendor: 'Google Inc.',
|
|
15
|
+
language: 'zh-CN',
|
|
16
|
+
languages: ['zh-CN', 'zh', 'en'],
|
|
17
|
+
onLine: true,
|
|
18
|
+
cookieEnabled: true,
|
|
19
|
+
hardwareConcurrency: 8,
|
|
20
|
+
maxTouchPoints: 0,
|
|
21
|
+
deviceMemory: 8,
|
|
22
|
+
webdriver: false,
|
|
23
|
+
doNotTrack: null,
|
|
24
|
+
|
|
25
|
+
plugins: { length: 0, item: () => null, namedItem: () => null, refresh: () => {} },
|
|
26
|
+
mimeTypes: { length: 0, item: () => null, namedItem: () => null },
|
|
27
|
+
|
|
28
|
+
connection: {
|
|
29
|
+
downlink: 10, effectiveType: '4g', rtt: 50, saveData: false,
|
|
30
|
+
addEventListener: () => {}, removeEventListener: () => {}
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
geolocation: {
|
|
34
|
+
getCurrentPosition: (s, e) => e && e({ code: 1, message: 'Denied' }),
|
|
35
|
+
watchPosition: () => 0,
|
|
36
|
+
clearWatch: () => {}
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
permissions: {
|
|
40
|
+
query: (d) => Promise.resolve({ name: d.name, state: 'prompt' })
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
javaEnabled: () => false,
|
|
44
|
+
vibrate: () => true,
|
|
45
|
+
sendBeacon: () => true,
|
|
46
|
+
|
|
47
|
+
getBattery: () => Promise.resolve({
|
|
48
|
+
charging: true, chargingTime: 0, dischargingTime: Infinity, level: 1
|
|
49
|
+
}),
|
|
50
|
+
|
|
51
|
+
clipboard: {
|
|
52
|
+
read: () => Promise.reject(new Error('NotAllowed')),
|
|
53
|
+
readText: () => Promise.reject(new Error('NotAllowed')),
|
|
54
|
+
write: () => Promise.reject(new Error('NotAllowed')),
|
|
55
|
+
writeText: () => Promise.reject(new Error('NotAllowed'))
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
userAgentData: {
|
|
59
|
+
brands: [{ brand: 'Chromium', version: '120' }],
|
|
60
|
+
mobile: false,
|
|
61
|
+
platform: 'Windows',
|
|
62
|
+
getHighEntropyValues: () => Promise.resolve({ platform: 'Windows', architecture: 'x86' })
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
window.navigator = navigator;
|
|
67
|
+
})();
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
export default navigatorCode;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSpider - Screen 环境模块
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const screenCode = `
|
|
6
|
+
(function() {
|
|
7
|
+
window.screen = {
|
|
8
|
+
width: 1920,
|
|
9
|
+
height: 1080,
|
|
10
|
+
availWidth: 1920,
|
|
11
|
+
availHeight: 1040,
|
|
12
|
+
colorDepth: 24,
|
|
13
|
+
pixelDepth: 24,
|
|
14
|
+
availLeft: 0,
|
|
15
|
+
availTop: 0,
|
|
16
|
+
orientation: {
|
|
17
|
+
type: 'landscape-primary',
|
|
18
|
+
angle: 0,
|
|
19
|
+
addEventListener: () => {},
|
|
20
|
+
removeEventListener: () => {}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
export default screenCode;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSpider - Storage 环境模块
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const storageCode = `
|
|
6
|
+
(function() {
|
|
7
|
+
function createStorage() {
|
|
8
|
+
const data = {};
|
|
9
|
+
return {
|
|
10
|
+
get length() { return Object.keys(data).length; },
|
|
11
|
+
key: function(i) { return Object.keys(data)[i] || null; },
|
|
12
|
+
getItem: function(k) { return data[k] ?? null; },
|
|
13
|
+
setItem: function(k, v) { data[k] = String(v); },
|
|
14
|
+
removeItem: function(k) { delete data[k]; },
|
|
15
|
+
clear: function() { for (let k in data) delete data[k]; }
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
window.localStorage = createStorage();
|
|
19
|
+
window.sessionStorage = createStorage();
|
|
20
|
+
})();
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
export default storageCode;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSpider - Document 环境模块(基础版)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const documentCode = `
|
|
6
|
+
(function() {
|
|
7
|
+
// 元素ID生成器
|
|
8
|
+
let __elId = 0;
|
|
9
|
+
const __elements = new Map();
|
|
10
|
+
|
|
11
|
+
// 基础元素类
|
|
12
|
+
function Element(tagName) {
|
|
13
|
+
this.__id__ = 'el_' + (++__elId);
|
|
14
|
+
this.tagName = tagName.toUpperCase();
|
|
15
|
+
this.nodeName = this.tagName;
|
|
16
|
+
this.nodeType = 1;
|
|
17
|
+
this.children = [];
|
|
18
|
+
this.childNodes = [];
|
|
19
|
+
this.parentNode = null;
|
|
20
|
+
this.parentElement = null;
|
|
21
|
+
this.attributes = {};
|
|
22
|
+
this._attrs = {};
|
|
23
|
+
this.style = {};
|
|
24
|
+
this.classList = {
|
|
25
|
+
_list: [],
|
|
26
|
+
add: function(...c) { c.forEach(x => !this._list.includes(x) && this._list.push(x)); },
|
|
27
|
+
remove: function(...c) { c.forEach(x => { const i = this._list.indexOf(x); if (i > -1) this._list.splice(i, 1); }); },
|
|
28
|
+
contains: function(c) { return this._list.includes(c); },
|
|
29
|
+
toggle: function(c) { this.contains(c) ? this.remove(c) : this.add(c); }
|
|
30
|
+
};
|
|
31
|
+
this.innerHTML = '';
|
|
32
|
+
this.innerText = '';
|
|
33
|
+
this.textContent = '';
|
|
34
|
+
this.id = '';
|
|
35
|
+
this.className = '';
|
|
36
|
+
this.dataset = {};
|
|
37
|
+
__elements.set(this.__id__, this);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
Element.prototype = {
|
|
41
|
+
getAttribute: function(n) { return this._attrs[n] ?? null; },
|
|
42
|
+
setAttribute: function(n, v) { this._attrs[n] = String(v); if (n === 'id') this.id = v; },
|
|
43
|
+
removeAttribute: function(n) { delete this._attrs[n]; },
|
|
44
|
+
hasAttribute: function(n) { return n in this._attrs; },
|
|
45
|
+
appendChild: function(c) { this.children.push(c); this.childNodes.push(c); c.parentNode = this; return c; },
|
|
46
|
+
removeChild: function(c) {
|
|
47
|
+
const i = this.children.indexOf(c);
|
|
48
|
+
if (i > -1) { this.children.splice(i, 1); this.childNodes.splice(i, 1); }
|
|
49
|
+
return c;
|
|
50
|
+
},
|
|
51
|
+
querySelector: function(s) { return null; },
|
|
52
|
+
querySelectorAll: function(s) { return []; },
|
|
53
|
+
getElementsByTagName: function(t) { return []; },
|
|
54
|
+
getElementsByClassName: function(c) { return []; },
|
|
55
|
+
addEventListener: function(t, h, o) {},
|
|
56
|
+
removeEventListener: function(t, h, o) {},
|
|
57
|
+
dispatchEvent: function(e) { return true; },
|
|
58
|
+
cloneNode: function(deep) { return new Element(this.tagName); },
|
|
59
|
+
getBoundingClientRect: function() {
|
|
60
|
+
return { top: 0, left: 0, right: 0, bottom: 0, width: 0, height: 0, x: 0, y: 0 };
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Document 对象
|
|
65
|
+
const document = {
|
|
66
|
+
nodeType: 9,
|
|
67
|
+
nodeName: '#document',
|
|
68
|
+
documentElement: new Element('HTML'),
|
|
69
|
+
head: new Element('HEAD'),
|
|
70
|
+
body: new Element('BODY'),
|
|
71
|
+
title: '',
|
|
72
|
+
cookie: '',
|
|
73
|
+
domain: 'example.com',
|
|
74
|
+
URL: 'https://example.com/',
|
|
75
|
+
referrer: '',
|
|
76
|
+
readyState: 'complete',
|
|
77
|
+
hidden: false,
|
|
78
|
+
visibilityState: 'visible',
|
|
79
|
+
|
|
80
|
+
createElement: function(tag) { return new Element(tag); },
|
|
81
|
+
createTextNode: function(text) { return { nodeType: 3, textContent: text }; },
|
|
82
|
+
createDocumentFragment: function() { return new Element('fragment'); },
|
|
83
|
+
createComment: function(text) { return { nodeType: 8, textContent: text }; },
|
|
84
|
+
|
|
85
|
+
getElementById: function(id) { return null; },
|
|
86
|
+
getElementsByTagName: function(tag) { return []; },
|
|
87
|
+
getElementsByClassName: function(cls) { return []; },
|
|
88
|
+
getElementsByName: function(name) { return []; },
|
|
89
|
+
querySelector: function(sel) { return null; },
|
|
90
|
+
querySelectorAll: function(sel) { return []; },
|
|
91
|
+
|
|
92
|
+
addEventListener: function() {},
|
|
93
|
+
removeEventListener: function() {},
|
|
94
|
+
dispatchEvent: function() { return true; },
|
|
95
|
+
|
|
96
|
+
write: function() {},
|
|
97
|
+
writeln: function() {},
|
|
98
|
+
open: function() {},
|
|
99
|
+
close: function() {}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
document.documentElement.appendChild(document.head);
|
|
103
|
+
document.documentElement.appendChild(document.body);
|
|
104
|
+
|
|
105
|
+
window.document = document;
|
|
106
|
+
window.Element = Element;
|
|
107
|
+
})();
|
|
108
|
+
`;
|
|
109
|
+
|
|
110
|
+
export default documentCode;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSpider - Event 环境模块
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const eventCode = `
|
|
6
|
+
(function() {
|
|
7
|
+
function Event(type, options = {}) {
|
|
8
|
+
this.type = type;
|
|
9
|
+
this.bubbles = options.bubbles || false;
|
|
10
|
+
this.cancelable = options.cancelable || false;
|
|
11
|
+
this.defaultPrevented = false;
|
|
12
|
+
this.target = null;
|
|
13
|
+
this.currentTarget = null;
|
|
14
|
+
this.timeStamp = Date.now();
|
|
15
|
+
}
|
|
16
|
+
Event.prototype = {
|
|
17
|
+
preventDefault: function() { this.defaultPrevented = true; },
|
|
18
|
+
stopPropagation: function() {},
|
|
19
|
+
stopImmediatePropagation: function() {}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function CustomEvent(type, options = {}) {
|
|
23
|
+
Event.call(this, type, options);
|
|
24
|
+
this.detail = options.detail || null;
|
|
25
|
+
}
|
|
26
|
+
CustomEvent.prototype = Object.create(Event.prototype);
|
|
27
|
+
|
|
28
|
+
function MouseEvent(type, options = {}) {
|
|
29
|
+
Event.call(this, type, options);
|
|
30
|
+
this.clientX = options.clientX || 0;
|
|
31
|
+
this.clientY = options.clientY || 0;
|
|
32
|
+
this.button = options.button || 0;
|
|
33
|
+
}
|
|
34
|
+
MouseEvent.prototype = Object.create(Event.prototype);
|
|
35
|
+
|
|
36
|
+
function KeyboardEvent(type, options = {}) {
|
|
37
|
+
Event.call(this, type, options);
|
|
38
|
+
this.key = options.key || '';
|
|
39
|
+
this.code = options.code || '';
|
|
40
|
+
this.keyCode = options.keyCode || 0;
|
|
41
|
+
}
|
|
42
|
+
KeyboardEvent.prototype = Object.create(Event.prototype);
|
|
43
|
+
|
|
44
|
+
window.Event = Event;
|
|
45
|
+
window.CustomEvent = CustomEvent;
|
|
46
|
+
window.MouseEvent = MouseEvent;
|
|
47
|
+
window.KeyboardEvent = KeyboardEvent;
|
|
48
|
+
})();
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
export default eventCode;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSpider - 环境模块索引
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { navigatorCode } from './bom/navigator.js';
|
|
6
|
+
import { locationCode } from './bom/location.js';
|
|
7
|
+
import { screenCode } from './bom/screen.js';
|
|
8
|
+
import { historyCode } from './bom/history.js';
|
|
9
|
+
import { storageCode } from './bom/storage.js';
|
|
10
|
+
import { documentCode } from './dom/document.js';
|
|
11
|
+
import { eventCode } from './dom/event.js';
|
|
12
|
+
import { fetchCode } from './webapi/fetch.js';
|
|
13
|
+
import { xhrCode } from './webapi/xhr.js';
|
|
14
|
+
import { urlCode } from './webapi/url.js';
|
|
15
|
+
|
|
16
|
+
export const modules = {
|
|
17
|
+
navigator: navigatorCode,
|
|
18
|
+
location: locationCode,
|
|
19
|
+
screen: screenCode,
|
|
20
|
+
history: historyCode,
|
|
21
|
+
storage: storageCode,
|
|
22
|
+
document: documentCode,
|
|
23
|
+
event: eventCode,
|
|
24
|
+
fetch: fetchCode,
|
|
25
|
+
xhr: xhrCode,
|
|
26
|
+
url: urlCode
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const loadOrder = [
|
|
30
|
+
'event', 'document', 'navigator', 'location',
|
|
31
|
+
'screen', 'history', 'storage', 'url', 'fetch', 'xhr'
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
export default modules;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSpider - Fetch 环境模块
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const fetchCode = `
|
|
6
|
+
(function() {
|
|
7
|
+
window.fetch = function(url, options = {}) {
|
|
8
|
+
console.log('[fetch]', url);
|
|
9
|
+
return Promise.resolve({
|
|
10
|
+
ok: true,
|
|
11
|
+
status: 200,
|
|
12
|
+
statusText: 'OK',
|
|
13
|
+
url: url,
|
|
14
|
+
headers: new Map(),
|
|
15
|
+
json: () => Promise.resolve({}),
|
|
16
|
+
text: () => Promise.resolve(''),
|
|
17
|
+
blob: () => Promise.resolve(new Blob()),
|
|
18
|
+
arrayBuffer: () => Promise.resolve(new ArrayBuffer(0)),
|
|
19
|
+
clone: function() { return this; }
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
window.Headers = function(init) {
|
|
24
|
+
this._h = new Map();
|
|
25
|
+
if (init) Object.entries(init).forEach(([k,v]) => this._h.set(k,v));
|
|
26
|
+
};
|
|
27
|
+
window.Headers.prototype = {
|
|
28
|
+
get: function(k) { return this._h.get(k); },
|
|
29
|
+
set: function(k,v) { this._h.set(k,v); },
|
|
30
|
+
has: function(k) { return this._h.has(k); },
|
|
31
|
+
delete: function(k) { this._h.delete(k); }
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
window.Request = function(url, options) {
|
|
35
|
+
this.url = url;
|
|
36
|
+
this.method = options?.method || 'GET';
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
window.Response = function(body, options) {
|
|
40
|
+
this.body = body;
|
|
41
|
+
this.status = options?.status || 200;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
export default fetchCode;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSpider - URL 环境模块
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const urlCode = `
|
|
6
|
+
(function() {
|
|
7
|
+
function URL(url, base) {
|
|
8
|
+
const full = base ? new URL(base).origin + url : url;
|
|
9
|
+
const m = full.match(/^(\\w+):\\/\\/([^/:]+)(:\\d+)?([^?#]*)(\\?[^#]*)?(#.*)?$/);
|
|
10
|
+
this.href = full;
|
|
11
|
+
this.protocol = (m?.[1] || 'https') + ':';
|
|
12
|
+
this.host = (m?.[2] || '') + (m?.[3] || '');
|
|
13
|
+
this.hostname = m?.[2] || '';
|
|
14
|
+
this.port = (m?.[3] || '').slice(1);
|
|
15
|
+
this.pathname = m?.[4] || '/';
|
|
16
|
+
this.search = m?.[5] || '';
|
|
17
|
+
this.hash = m?.[6] || '';
|
|
18
|
+
this.origin = this.protocol + '//' + this.host;
|
|
19
|
+
this.searchParams = new URLSearchParams(this.search);
|
|
20
|
+
}
|
|
21
|
+
URL.prototype.toString = function() { return this.href; };
|
|
22
|
+
|
|
23
|
+
function URLSearchParams(init) {
|
|
24
|
+
this._p = new Map();
|
|
25
|
+
if (typeof init === 'string') {
|
|
26
|
+
init.replace(/^\\?/, '').split('&').forEach(p => {
|
|
27
|
+
const [k,v] = p.split('=');
|
|
28
|
+
if (k) this._p.set(decodeURIComponent(k), decodeURIComponent(v || ''));
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
URLSearchParams.prototype = {
|
|
33
|
+
get: function(k) { return this._p.get(k) || null; },
|
|
34
|
+
set: function(k,v) { this._p.set(k,v); },
|
|
35
|
+
has: function(k) { return this._p.has(k); },
|
|
36
|
+
delete: function(k) { this._p.delete(k); },
|
|
37
|
+
toString: function() {
|
|
38
|
+
return Array.from(this._p).map(([k,v]) => k+'='+encodeURIComponent(v)).join('&');
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
window.URL = URL;
|
|
43
|
+
window.URLSearchParams = URLSearchParams;
|
|
44
|
+
})();
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
export default urlCode;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSpider - XMLHttpRequest 环境模块
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const xhrCode = `
|
|
6
|
+
(function() {
|
|
7
|
+
function XMLHttpRequest() {
|
|
8
|
+
this.readyState = 0;
|
|
9
|
+
this.status = 0;
|
|
10
|
+
this.statusText = '';
|
|
11
|
+
this.responseText = '';
|
|
12
|
+
this.responseXML = null;
|
|
13
|
+
this.response = '';
|
|
14
|
+
this.responseType = '';
|
|
15
|
+
this.timeout = 0;
|
|
16
|
+
this.withCredentials = false;
|
|
17
|
+
this._headers = {};
|
|
18
|
+
this._url = '';
|
|
19
|
+
this._method = 'GET';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
XMLHttpRequest.prototype = {
|
|
23
|
+
UNSENT: 0, OPENED: 1, HEADERS_RECEIVED: 2, LOADING: 3, DONE: 4,
|
|
24
|
+
open: function(method, url) {
|
|
25
|
+
this._method = method;
|
|
26
|
+
this._url = url;
|
|
27
|
+
this.readyState = 1;
|
|
28
|
+
},
|
|
29
|
+
setRequestHeader: function(k, v) { this._headers[k] = v; },
|
|
30
|
+
getResponseHeader: function(k) { return null; },
|
|
31
|
+
getAllResponseHeaders: function() { return ''; },
|
|
32
|
+
send: function(data) {
|
|
33
|
+
console.log('[DeepSpider:xhr]', this._method, this._url);
|
|
34
|
+
this.readyState = 4;
|
|
35
|
+
this.status = 200;
|
|
36
|
+
if (this.onreadystatechange) this.onreadystatechange();
|
|
37
|
+
if (this.onload) this.onload();
|
|
38
|
+
},
|
|
39
|
+
abort: function() { this.readyState = 0; },
|
|
40
|
+
addEventListener: function() {},
|
|
41
|
+
removeEventListener: function() {}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
window.XMLHttpRequest = XMLHttpRequest;
|
|
45
|
+
})();
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
export default xhrCode;
|