recker 1.0.20-next.963881c → 1.0.20-next.a5aea1e

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 (360) hide show
  1. package/dist/ai/adaptive-timeout.d.ts +0 -1
  2. package/dist/ai/client.d.ts +0 -1
  3. package/dist/ai/index.d.ts +0 -1
  4. package/dist/ai/providers/anthropic.d.ts +0 -1
  5. package/dist/ai/providers/base.d.ts +0 -1
  6. package/dist/ai/providers/index.d.ts +0 -1
  7. package/dist/ai/providers/openai.d.ts +0 -1
  8. package/dist/ai/rate-limiter.d.ts +0 -1
  9. package/dist/bench/generator.d.ts +0 -1
  10. package/dist/bench/stats.d.ts +0 -1
  11. package/dist/cache/file-storage.d.ts +0 -1
  12. package/dist/cache/memory-limits.d.ts +0 -1
  13. package/dist/cache/memory-storage.d.ts +0 -1
  14. package/dist/cache/redis-storage.d.ts +0 -1
  15. package/dist/cli/handler.d.ts +2 -1
  16. package/dist/cli/handler.js +36 -5
  17. package/dist/cli/index.d.ts +0 -1
  18. package/dist/cli/index.js +214 -3
  19. package/dist/cli/presets.d.ts +0 -1
  20. package/dist/cli/tui/ai-chat.d.ts +0 -1
  21. package/dist/cli/tui/load-dashboard.d.ts +0 -1
  22. package/dist/cli/tui/scroll-buffer.d.ts +0 -1
  23. package/dist/cli/tui/search-panel.d.ts +0 -1
  24. package/dist/cli/tui/shell-search.d.ts +0 -1
  25. package/dist/cli/tui/shell.d.ts +0 -1
  26. package/dist/cli/tui/websocket.d.ts +0 -1
  27. package/dist/constants/http-status.d.ts +0 -1
  28. package/dist/constants.d.ts +0 -1
  29. package/dist/contract/index.d.ts +0 -1
  30. package/dist/cookies/memory-cookie-jar.d.ts +0 -1
  31. package/dist/core/client.d.ts +0 -1
  32. package/dist/core/errors.d.ts +0 -1
  33. package/dist/core/index.d.ts +0 -1
  34. package/dist/core/request-promise.d.ts +0 -1
  35. package/dist/core/request.d.ts +0 -1
  36. package/dist/core/response.d.ts +0 -1
  37. package/dist/dns/index.d.ts +0 -1
  38. package/dist/dns/propagation.d.ts +0 -1
  39. package/dist/events/request-events.d.ts +0 -1
  40. package/dist/index.d.ts +0 -1
  41. package/dist/mcp/client.d.ts +0 -1
  42. package/dist/mcp/contract.d.ts +0 -1
  43. package/dist/mcp/embeddings-loader.d.ts +0 -1
  44. package/dist/mcp/geoip-loader.d.ts +0 -1
  45. package/dist/mcp/index.d.ts +0 -1
  46. package/dist/mcp/ip-intel.d.ts +0 -1
  47. package/dist/mcp/search/hybrid-search.d.ts +0 -1
  48. package/dist/mcp/search/index.d.ts +0 -1
  49. package/dist/mcp/search/math.d.ts +0 -1
  50. package/dist/mcp/search/types.d.ts +0 -1
  51. package/dist/mcp/server.d.ts +0 -1
  52. package/dist/mcp/types.d.ts +0 -1
  53. package/dist/plugins/auth.d.ts +0 -1
  54. package/dist/plugins/cache.d.ts +0 -1
  55. package/dist/plugins/circuit-breaker.d.ts +0 -1
  56. package/dist/plugins/compression.d.ts +0 -1
  57. package/dist/plugins/cookie-jar.d.ts +0 -1
  58. package/dist/plugins/dedup.d.ts +0 -1
  59. package/dist/plugins/graphql.d.ts +0 -1
  60. package/dist/plugins/grpc-web.d.ts +0 -1
  61. package/dist/plugins/har-player.d.ts +0 -1
  62. package/dist/plugins/har-recorder.d.ts +0 -1
  63. package/dist/plugins/hls.d.ts +0 -1
  64. package/dist/plugins/http2-push.d.ts +0 -1
  65. package/dist/plugins/http3.d.ts +0 -1
  66. package/dist/plugins/interface-rotator.d.ts +0 -1
  67. package/dist/plugins/jsonrpc.d.ts +0 -1
  68. package/dist/plugins/logger.d.ts +0 -1
  69. package/dist/plugins/odata.d.ts +0 -1
  70. package/dist/plugins/pagination.d.ts +0 -1
  71. package/dist/plugins/proxy-rotator.d.ts +0 -1
  72. package/dist/plugins/retry.d.ts +0 -1
  73. package/dist/plugins/scrape.d.ts +0 -1
  74. package/dist/plugins/server-timing.d.ts +0 -1
  75. package/dist/plugins/soap.d.ts +0 -1
  76. package/dist/plugins/user-agent.d.ts +0 -1
  77. package/dist/plugins/xml.d.ts +0 -1
  78. package/dist/plugins/xsrf.d.ts +0 -1
  79. package/dist/presets/anthropic.d.ts +0 -1
  80. package/dist/presets/aws.d.ts +0 -1
  81. package/dist/presets/azure-openai.d.ts +0 -1
  82. package/dist/presets/azure.d.ts +0 -1
  83. package/dist/presets/cloudflare.d.ts +0 -1
  84. package/dist/presets/cohere.d.ts +0 -1
  85. package/dist/presets/deepseek.d.ts +0 -1
  86. package/dist/presets/digitalocean.d.ts +0 -1
  87. package/dist/presets/discord.d.ts +0 -1
  88. package/dist/presets/fireworks.d.ts +0 -1
  89. package/dist/presets/gcp.d.ts +0 -1
  90. package/dist/presets/gemini.d.ts +0 -1
  91. package/dist/presets/github.d.ts +0 -1
  92. package/dist/presets/gitlab.d.ts +0 -1
  93. package/dist/presets/groq.d.ts +0 -1
  94. package/dist/presets/huggingface.d.ts +0 -1
  95. package/dist/presets/index.d.ts +0 -1
  96. package/dist/presets/linear.d.ts +0 -1
  97. package/dist/presets/mailgun.d.ts +0 -1
  98. package/dist/presets/meta.d.ts +0 -1
  99. package/dist/presets/mistral.d.ts +0 -1
  100. package/dist/presets/notion.d.ts +0 -1
  101. package/dist/presets/openai.d.ts +0 -1
  102. package/dist/presets/oracle.d.ts +0 -1
  103. package/dist/presets/perplexity.d.ts +0 -1
  104. package/dist/presets/registry.d.ts +0 -1
  105. package/dist/presets/replicate.d.ts +0 -1
  106. package/dist/presets/sinch.d.ts +0 -1
  107. package/dist/presets/slack.d.ts +0 -1
  108. package/dist/presets/stripe.d.ts +0 -1
  109. package/dist/presets/supabase.d.ts +0 -1
  110. package/dist/presets/tiktok.d.ts +0 -1
  111. package/dist/presets/together.d.ts +0 -1
  112. package/dist/presets/twilio.d.ts +0 -1
  113. package/dist/presets/vercel.d.ts +0 -1
  114. package/dist/presets/vultr.d.ts +0 -1
  115. package/dist/presets/xai.d.ts +0 -1
  116. package/dist/presets/youtube.d.ts +0 -1
  117. package/dist/protocols/ftp.d.ts +0 -1
  118. package/dist/protocols/index.d.ts +0 -1
  119. package/dist/protocols/sftp.d.ts +0 -1
  120. package/dist/protocols/telnet.d.ts +0 -1
  121. package/dist/recker.d.ts +0 -1
  122. package/dist/runner/request-runner.d.ts +0 -1
  123. package/dist/scrape/document.d.ts +0 -1
  124. package/dist/scrape/element.d.ts +0 -1
  125. package/dist/scrape/extractors.d.ts +0 -1
  126. package/dist/scrape/index.d.ts +0 -1
  127. package/dist/scrape/types.d.ts +0 -1
  128. package/dist/testing/index.d.ts +8 -1
  129. package/dist/testing/index.js +4 -0
  130. package/dist/testing/mock-dns-server.d.ts +69 -0
  131. package/dist/testing/mock-dns-server.js +269 -0
  132. package/dist/testing/mock-ftp-server.d.ts +89 -0
  133. package/dist/testing/mock-ftp-server.js +562 -0
  134. package/dist/testing/mock-hls-server.d.ts +0 -1
  135. package/dist/testing/mock-http-server.d.ts +0 -1
  136. package/dist/testing/mock-sse-server.d.ts +0 -1
  137. package/dist/testing/mock-telnet-server.d.ts +59 -0
  138. package/dist/testing/mock-telnet-server.js +273 -0
  139. package/dist/testing/mock-udp-server.d.ts +0 -1
  140. package/dist/testing/mock-websocket-server.d.ts +0 -1
  141. package/dist/testing/mock-whois-server.d.ts +56 -0
  142. package/dist/testing/mock-whois-server.js +234 -0
  143. package/dist/testing/mock.d.ts +0 -1
  144. package/dist/transport/base-udp.d.ts +0 -1
  145. package/dist/transport/fetch.d.ts +0 -1
  146. package/dist/transport/udp-response.d.ts +0 -1
  147. package/dist/transport/udp.d.ts +0 -1
  148. package/dist/transport/undici.d.ts +0 -1
  149. package/dist/types/ai.d.ts +0 -1
  150. package/dist/types/index.d.ts +0 -1
  151. package/dist/types/logger.d.ts +0 -1
  152. package/dist/types/udp.d.ts +0 -1
  153. package/dist/udp/index.d.ts +0 -1
  154. package/dist/utils/agent-manager.d.ts +0 -1
  155. package/dist/utils/body.d.ts +0 -1
  156. package/dist/utils/cert.d.ts +0 -1
  157. package/dist/utils/charset.d.ts +0 -1
  158. package/dist/utils/chart.d.ts +0 -1
  159. package/dist/utils/client-pool.d.ts +0 -1
  160. package/dist/utils/colors.d.ts +0 -1
  161. package/dist/utils/concurrency.d.ts +0 -1
  162. package/dist/utils/dns-toolkit.d.ts +0 -1
  163. package/dist/utils/dns.d.ts +0 -1
  164. package/dist/utils/doh.d.ts +0 -1
  165. package/dist/utils/download.d.ts +0 -1
  166. package/dist/utils/env-proxy.d.ts +0 -1
  167. package/dist/utils/header-parser.d.ts +0 -1
  168. package/dist/utils/html-cleaner.d.ts +0 -1
  169. package/dist/utils/link-header.d.ts +0 -1
  170. package/dist/utils/optional-require.d.ts +0 -1
  171. package/dist/utils/progress.d.ts +0 -1
  172. package/dist/utils/rdap.d.ts +0 -1
  173. package/dist/utils/request-pool.d.ts +0 -1
  174. package/dist/utils/security-grader.d.ts +0 -1
  175. package/dist/utils/sparkline.d.ts +0 -1
  176. package/dist/utils/sse.d.ts +0 -1
  177. package/dist/utils/streaming.d.ts +0 -1
  178. package/dist/utils/system-metrics.d.ts +0 -1
  179. package/dist/utils/tls-inspector.d.ts +0 -1
  180. package/dist/utils/try-fn.d.ts +0 -1
  181. package/dist/utils/upload.d.ts +0 -1
  182. package/dist/utils/user-agent.d.ts +0 -1
  183. package/dist/utils/whois.d.ts +0 -1
  184. package/dist/webrtc/index.d.ts +0 -1
  185. package/dist/websocket/client.d.ts +0 -1
  186. package/package.json +1 -1
  187. package/dist/ai/adaptive-timeout.d.ts.map +0 -1
  188. package/dist/ai/client.d.ts.map +0 -1
  189. package/dist/ai/index.d.ts.map +0 -1
  190. package/dist/ai/providers/anthropic.d.ts.map +0 -1
  191. package/dist/ai/providers/base.d.ts.map +0 -1
  192. package/dist/ai/providers/index.d.ts.map +0 -1
  193. package/dist/ai/providers/openai.d.ts.map +0 -1
  194. package/dist/ai/rate-limiter.d.ts.map +0 -1
  195. package/dist/bench/generator.d.ts.map +0 -1
  196. package/dist/bench/stats.d.ts.map +0 -1
  197. package/dist/cache/file-storage.d.ts.map +0 -1
  198. package/dist/cache/memory-limits.d.ts.map +0 -1
  199. package/dist/cache/memory-storage.d.ts.map +0 -1
  200. package/dist/cache/redis-storage.d.ts.map +0 -1
  201. package/dist/cli/handler.d.ts.map +0 -1
  202. package/dist/cli/index.d.ts.map +0 -1
  203. package/dist/cli/presets.d.ts.map +0 -1
  204. package/dist/cli/tui/ai-chat.d.ts.map +0 -1
  205. package/dist/cli/tui/load-dashboard.d.ts.map +0 -1
  206. package/dist/cli/tui/scroll-buffer.d.ts.map +0 -1
  207. package/dist/cli/tui/search-panel.d.ts.map +0 -1
  208. package/dist/cli/tui/shell-search.d.ts.map +0 -1
  209. package/dist/cli/tui/shell.d.ts.map +0 -1
  210. package/dist/cli/tui/websocket.d.ts.map +0 -1
  211. package/dist/constants/http-status.d.ts.map +0 -1
  212. package/dist/constants.d.ts.map +0 -1
  213. package/dist/contract/index.d.ts.map +0 -1
  214. package/dist/cookies/memory-cookie-jar.d.ts.map +0 -1
  215. package/dist/core/client.d.ts.map +0 -1
  216. package/dist/core/errors.d.ts.map +0 -1
  217. package/dist/core/index.d.ts.map +0 -1
  218. package/dist/core/request-promise.d.ts.map +0 -1
  219. package/dist/core/request.d.ts.map +0 -1
  220. package/dist/core/response.d.ts.map +0 -1
  221. package/dist/dns/index.d.ts.map +0 -1
  222. package/dist/dns/propagation.d.ts.map +0 -1
  223. package/dist/events/request-events.d.ts.map +0 -1
  224. package/dist/index.d.ts.map +0 -1
  225. package/dist/mcp/client.d.ts.map +0 -1
  226. package/dist/mcp/contract.d.ts.map +0 -1
  227. package/dist/mcp/embeddings-loader.d.ts.map +0 -1
  228. package/dist/mcp/geoip-loader.d.ts.map +0 -1
  229. package/dist/mcp/index.d.ts.map +0 -1
  230. package/dist/mcp/ip-intel.d.ts.map +0 -1
  231. package/dist/mcp/search/hybrid-search.d.ts.map +0 -1
  232. package/dist/mcp/search/index.d.ts.map +0 -1
  233. package/dist/mcp/search/math.d.ts.map +0 -1
  234. package/dist/mcp/search/types.d.ts.map +0 -1
  235. package/dist/mcp/server.d.ts.map +0 -1
  236. package/dist/mcp/types.d.ts.map +0 -1
  237. package/dist/plugins/auth.d.ts.map +0 -1
  238. package/dist/plugins/cache.d.ts.map +0 -1
  239. package/dist/plugins/circuit-breaker.d.ts.map +0 -1
  240. package/dist/plugins/compression.d.ts.map +0 -1
  241. package/dist/plugins/cookie-jar.d.ts.map +0 -1
  242. package/dist/plugins/dedup.d.ts.map +0 -1
  243. package/dist/plugins/graphql.d.ts.map +0 -1
  244. package/dist/plugins/grpc-web.d.ts.map +0 -1
  245. package/dist/plugins/har-player.d.ts.map +0 -1
  246. package/dist/plugins/har-recorder.d.ts.map +0 -1
  247. package/dist/plugins/hls.d.ts.map +0 -1
  248. package/dist/plugins/http2-push.d.ts.map +0 -1
  249. package/dist/plugins/http3.d.ts.map +0 -1
  250. package/dist/plugins/interface-rotator.d.ts.map +0 -1
  251. package/dist/plugins/jsonrpc.d.ts.map +0 -1
  252. package/dist/plugins/logger.d.ts.map +0 -1
  253. package/dist/plugins/odata.d.ts.map +0 -1
  254. package/dist/plugins/pagination.d.ts.map +0 -1
  255. package/dist/plugins/proxy-rotator.d.ts.map +0 -1
  256. package/dist/plugins/retry.d.ts.map +0 -1
  257. package/dist/plugins/scrape.d.ts.map +0 -1
  258. package/dist/plugins/server-timing.d.ts.map +0 -1
  259. package/dist/plugins/soap.d.ts.map +0 -1
  260. package/dist/plugins/user-agent.d.ts.map +0 -1
  261. package/dist/plugins/xml.d.ts.map +0 -1
  262. package/dist/plugins/xsrf.d.ts.map +0 -1
  263. package/dist/presets/anthropic.d.ts.map +0 -1
  264. package/dist/presets/aws.d.ts.map +0 -1
  265. package/dist/presets/azure-openai.d.ts.map +0 -1
  266. package/dist/presets/azure.d.ts.map +0 -1
  267. package/dist/presets/cloudflare.d.ts.map +0 -1
  268. package/dist/presets/cohere.d.ts.map +0 -1
  269. package/dist/presets/deepseek.d.ts.map +0 -1
  270. package/dist/presets/digitalocean.d.ts.map +0 -1
  271. package/dist/presets/discord.d.ts.map +0 -1
  272. package/dist/presets/fireworks.d.ts.map +0 -1
  273. package/dist/presets/gcp.d.ts.map +0 -1
  274. package/dist/presets/gemini.d.ts.map +0 -1
  275. package/dist/presets/github.d.ts.map +0 -1
  276. package/dist/presets/gitlab.d.ts.map +0 -1
  277. package/dist/presets/groq.d.ts.map +0 -1
  278. package/dist/presets/huggingface.d.ts.map +0 -1
  279. package/dist/presets/index.d.ts.map +0 -1
  280. package/dist/presets/linear.d.ts.map +0 -1
  281. package/dist/presets/mailgun.d.ts.map +0 -1
  282. package/dist/presets/meta.d.ts.map +0 -1
  283. package/dist/presets/mistral.d.ts.map +0 -1
  284. package/dist/presets/notion.d.ts.map +0 -1
  285. package/dist/presets/openai.d.ts.map +0 -1
  286. package/dist/presets/oracle.d.ts.map +0 -1
  287. package/dist/presets/perplexity.d.ts.map +0 -1
  288. package/dist/presets/registry.d.ts.map +0 -1
  289. package/dist/presets/replicate.d.ts.map +0 -1
  290. package/dist/presets/sinch.d.ts.map +0 -1
  291. package/dist/presets/slack.d.ts.map +0 -1
  292. package/dist/presets/stripe.d.ts.map +0 -1
  293. package/dist/presets/supabase.d.ts.map +0 -1
  294. package/dist/presets/tiktok.d.ts.map +0 -1
  295. package/dist/presets/together.d.ts.map +0 -1
  296. package/dist/presets/twilio.d.ts.map +0 -1
  297. package/dist/presets/vercel.d.ts.map +0 -1
  298. package/dist/presets/vultr.d.ts.map +0 -1
  299. package/dist/presets/xai.d.ts.map +0 -1
  300. package/dist/presets/youtube.d.ts.map +0 -1
  301. package/dist/protocols/ftp.d.ts.map +0 -1
  302. package/dist/protocols/index.d.ts.map +0 -1
  303. package/dist/protocols/sftp.d.ts.map +0 -1
  304. package/dist/protocols/telnet.d.ts.map +0 -1
  305. package/dist/recker.d.ts.map +0 -1
  306. package/dist/runner/request-runner.d.ts.map +0 -1
  307. package/dist/scrape/document.d.ts.map +0 -1
  308. package/dist/scrape/element.d.ts.map +0 -1
  309. package/dist/scrape/extractors.d.ts.map +0 -1
  310. package/dist/scrape/index.d.ts.map +0 -1
  311. package/dist/scrape/types.d.ts.map +0 -1
  312. package/dist/testing/index.d.ts.map +0 -1
  313. package/dist/testing/mock-hls-server.d.ts.map +0 -1
  314. package/dist/testing/mock-http-server.d.ts.map +0 -1
  315. package/dist/testing/mock-sse-server.d.ts.map +0 -1
  316. package/dist/testing/mock-udp-server.d.ts.map +0 -1
  317. package/dist/testing/mock-websocket-server.d.ts.map +0 -1
  318. package/dist/testing/mock.d.ts.map +0 -1
  319. package/dist/transport/base-udp.d.ts.map +0 -1
  320. package/dist/transport/fetch.d.ts.map +0 -1
  321. package/dist/transport/udp-response.d.ts.map +0 -1
  322. package/dist/transport/udp.d.ts.map +0 -1
  323. package/dist/transport/undici.d.ts.map +0 -1
  324. package/dist/types/ai.d.ts.map +0 -1
  325. package/dist/types/index.d.ts.map +0 -1
  326. package/dist/types/logger.d.ts.map +0 -1
  327. package/dist/types/udp.d.ts.map +0 -1
  328. package/dist/udp/index.d.ts.map +0 -1
  329. package/dist/utils/agent-manager.d.ts.map +0 -1
  330. package/dist/utils/body.d.ts.map +0 -1
  331. package/dist/utils/cert.d.ts.map +0 -1
  332. package/dist/utils/charset.d.ts.map +0 -1
  333. package/dist/utils/chart.d.ts.map +0 -1
  334. package/dist/utils/client-pool.d.ts.map +0 -1
  335. package/dist/utils/colors.d.ts.map +0 -1
  336. package/dist/utils/concurrency.d.ts.map +0 -1
  337. package/dist/utils/dns-toolkit.d.ts.map +0 -1
  338. package/dist/utils/dns.d.ts.map +0 -1
  339. package/dist/utils/doh.d.ts.map +0 -1
  340. package/dist/utils/download.d.ts.map +0 -1
  341. package/dist/utils/env-proxy.d.ts.map +0 -1
  342. package/dist/utils/header-parser.d.ts.map +0 -1
  343. package/dist/utils/html-cleaner.d.ts.map +0 -1
  344. package/dist/utils/link-header.d.ts.map +0 -1
  345. package/dist/utils/optional-require.d.ts.map +0 -1
  346. package/dist/utils/progress.d.ts.map +0 -1
  347. package/dist/utils/rdap.d.ts.map +0 -1
  348. package/dist/utils/request-pool.d.ts.map +0 -1
  349. package/dist/utils/security-grader.d.ts.map +0 -1
  350. package/dist/utils/sparkline.d.ts.map +0 -1
  351. package/dist/utils/sse.d.ts.map +0 -1
  352. package/dist/utils/streaming.d.ts.map +0 -1
  353. package/dist/utils/system-metrics.d.ts.map +0 -1
  354. package/dist/utils/tls-inspector.d.ts.map +0 -1
  355. package/dist/utils/try-fn.d.ts.map +0 -1
  356. package/dist/utils/upload.d.ts.map +0 -1
  357. package/dist/utils/user-agent.d.ts.map +0 -1
  358. package/dist/utils/whois.d.ts.map +0 -1
  359. package/dist/webrtc/index.d.ts.map +0 -1
  360. package/dist/websocket/client.d.ts.map +0 -1
@@ -0,0 +1,562 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import * as net from 'node:net';
3
+ export class MockFtpServer extends EventEmitter {
4
+ options;
5
+ server = null;
6
+ sessions = new Map();
7
+ files = new Map();
8
+ started = false;
9
+ sessionCounter = 0;
10
+ dataPortCounter = 30000;
11
+ stats = {
12
+ connectionsTotal: 0,
13
+ commandsReceived: 0,
14
+ filesDownloaded: 0,
15
+ filesUploaded: 0,
16
+ bytesTransferred: 0,
17
+ commandLog: [],
18
+ };
19
+ constructor(options = {}) {
20
+ super();
21
+ this.options = {
22
+ port: 2121,
23
+ host: '127.0.0.1',
24
+ anonymous: true,
25
+ username: 'user',
26
+ password: 'pass',
27
+ welcomeMessage: 'Welcome to Recker Mock FTP Server',
28
+ delay: 0,
29
+ ...options,
30
+ };
31
+ this.addDefaultFiles();
32
+ }
33
+ get isRunning() {
34
+ return this.started;
35
+ }
36
+ get port() {
37
+ return this.options.port;
38
+ }
39
+ get host() {
40
+ return this.options.host;
41
+ }
42
+ get url() {
43
+ return `ftp://${this.options.host}:${this.options.port}`;
44
+ }
45
+ get statistics() {
46
+ return { ...this.stats };
47
+ }
48
+ addFile(path, content) {
49
+ const normalizedPath = this.normalizePath(path);
50
+ const data = typeof content === 'string' ? content : content;
51
+ this.files.set(normalizedPath, {
52
+ content: data,
53
+ size: typeof content === 'string' ? Buffer.byteLength(content) : content.length,
54
+ modified: new Date(),
55
+ isDirectory: false,
56
+ });
57
+ const parts = normalizedPath.split('/').filter(Boolean);
58
+ let currentPath = '';
59
+ for (let i = 0; i < parts.length - 1; i++) {
60
+ currentPath += '/' + parts[i];
61
+ if (!this.files.has(currentPath)) {
62
+ this.files.set(currentPath, {
63
+ content: '',
64
+ size: 0,
65
+ modified: new Date(),
66
+ isDirectory: true,
67
+ });
68
+ }
69
+ }
70
+ }
71
+ addDirectory(path) {
72
+ const normalizedPath = this.normalizePath(path);
73
+ this.files.set(normalizedPath, {
74
+ content: '',
75
+ size: 0,
76
+ modified: new Date(),
77
+ isDirectory: true,
78
+ });
79
+ }
80
+ removeFile(path) {
81
+ return this.files.delete(this.normalizePath(path));
82
+ }
83
+ getFile(path) {
84
+ return this.files.get(this.normalizePath(path));
85
+ }
86
+ listDirectory(path) {
87
+ const normalizedPath = this.normalizePath(path);
88
+ const prefix = normalizedPath === '/' ? '/' : normalizedPath + '/';
89
+ const entries = [];
90
+ for (const [filePath] of this.files) {
91
+ if (filePath.startsWith(prefix) && filePath !== normalizedPath) {
92
+ const relative = filePath.substring(prefix.length);
93
+ const firstPart = relative.split('/')[0];
94
+ if (firstPart && !entries.includes(firstPart)) {
95
+ entries.push(firstPart);
96
+ }
97
+ }
98
+ }
99
+ return entries.sort();
100
+ }
101
+ clearFiles() {
102
+ this.files.clear();
103
+ this.addDefaultFiles();
104
+ }
105
+ normalizePath(path) {
106
+ let normalized = path.replace(/\/+/g, '/');
107
+ if (!normalized.startsWith('/')) {
108
+ normalized = '/' + normalized;
109
+ }
110
+ if (normalized.length > 1 && normalized.endsWith('/')) {
111
+ normalized = normalized.slice(0, -1);
112
+ }
113
+ return normalized;
114
+ }
115
+ addDefaultFiles() {
116
+ this.addDirectory('/');
117
+ this.addFile('/welcome.txt', 'Welcome to the FTP server!\nThis is a test file.');
118
+ this.addFile('/readme.md', '# Mock FTP Server\n\nThis is a mock FTP server for testing.');
119
+ this.addDirectory('/data');
120
+ this.addFile('/data/sample.json', JSON.stringify({ message: 'Hello', count: 42 }, null, 2));
121
+ this.addFile('/data/config.txt', 'host=localhost\nport=8080\ndebug=true');
122
+ this.addDirectory('/public');
123
+ this.addFile('/public/index.html', '<html><body><h1>Public Files</h1></body></html>');
124
+ }
125
+ async start() {
126
+ if (this.started) {
127
+ throw new Error('Server already started');
128
+ }
129
+ return new Promise((resolve, reject) => {
130
+ this.server = net.createServer((socket) => {
131
+ this.handleConnection(socket);
132
+ });
133
+ this.server.on('error', (err) => {
134
+ this.emit('error', err);
135
+ if (!this.started) {
136
+ reject(err);
137
+ }
138
+ });
139
+ this.server.listen(this.options.port, this.options.host, () => {
140
+ this.started = true;
141
+ this.emit('start');
142
+ resolve();
143
+ });
144
+ });
145
+ }
146
+ async stop() {
147
+ if (!this.started || !this.server)
148
+ return;
149
+ for (const session of this.sessions.values()) {
150
+ session.passiveSocket?.close();
151
+ session.socket.end();
152
+ }
153
+ this.sessions.clear();
154
+ return new Promise((resolve) => {
155
+ this.server.close(() => {
156
+ this.server = null;
157
+ this.started = false;
158
+ this.emit('stop');
159
+ resolve();
160
+ });
161
+ });
162
+ }
163
+ reset() {
164
+ this.stats = {
165
+ connectionsTotal: 0,
166
+ commandsReceived: 0,
167
+ filesDownloaded: 0,
168
+ filesUploaded: 0,
169
+ bytesTransferred: 0,
170
+ commandLog: [],
171
+ };
172
+ this.clearFiles();
173
+ this.emit('reset');
174
+ }
175
+ handleConnection(socket) {
176
+ const sessionId = `ftp-${++this.sessionCounter}`;
177
+ const session = {
178
+ id: sessionId,
179
+ socket,
180
+ authenticated: false,
181
+ username: null,
182
+ currentDir: '/',
183
+ transferMode: 'BINARY',
184
+ passiveSocket: null,
185
+ connectedAt: new Date(),
186
+ };
187
+ this.sessions.set(sessionId, session);
188
+ this.stats.connectionsTotal++;
189
+ this.emit('connect', session);
190
+ this.sendResponse(socket, 220, this.options.welcomeMessage);
191
+ let inputBuffer = '';
192
+ socket.on('data', async (data) => {
193
+ inputBuffer += data.toString('utf8');
194
+ while (inputBuffer.includes('\r\n')) {
195
+ const lineEnd = inputBuffer.indexOf('\r\n');
196
+ const line = inputBuffer.substring(0, lineEnd);
197
+ inputBuffer = inputBuffer.substring(lineEnd + 2);
198
+ if (line) {
199
+ await this.handleCommand(line, session);
200
+ }
201
+ }
202
+ });
203
+ socket.on('close', () => {
204
+ session.passiveSocket?.close();
205
+ this.sessions.delete(sessionId);
206
+ this.emit('disconnect', session);
207
+ });
208
+ socket.on('error', (err) => {
209
+ this.emit('error', err, session);
210
+ this.sessions.delete(sessionId);
211
+ });
212
+ }
213
+ async handleCommand(line, session) {
214
+ this.stats.commandsReceived++;
215
+ const spaceIndex = line.indexOf(' ');
216
+ const command = (spaceIndex > 0 ? line.substring(0, spaceIndex) : line).toUpperCase();
217
+ const args = spaceIndex > 0 ? line.substring(spaceIndex + 1) : '';
218
+ this.stats.commandLog.push({
219
+ command: line,
220
+ sessionId: session.id,
221
+ timestamp: Date.now(),
222
+ });
223
+ this.emit('command', command, args, session);
224
+ if (this.options.delay > 0) {
225
+ await new Promise((resolve) => setTimeout(resolve, this.options.delay));
226
+ }
227
+ switch (command) {
228
+ case 'USER':
229
+ this.handleUser(args, session);
230
+ break;
231
+ case 'PASS':
232
+ this.handlePass(args, session);
233
+ break;
234
+ case 'SYST':
235
+ this.sendResponse(session.socket, 215, 'UNIX Type: L8');
236
+ break;
237
+ case 'FEAT':
238
+ this.sendMultilineResponse(session.socket, 211, ['Features:', ' PASV', ' SIZE', ' UTF8', 'End']);
239
+ break;
240
+ case 'PWD':
241
+ case 'XPWD':
242
+ this.sendResponse(session.socket, 257, `"${session.currentDir}" is current directory`);
243
+ break;
244
+ case 'CWD':
245
+ case 'XCWD':
246
+ this.handleCwd(args, session);
247
+ break;
248
+ case 'CDUP':
249
+ case 'XCUP':
250
+ this.handleCdup(session);
251
+ break;
252
+ case 'TYPE':
253
+ this.handleType(args, session);
254
+ break;
255
+ case 'PASV':
256
+ await this.handlePasv(session);
257
+ break;
258
+ case 'LIST':
259
+ await this.handleList(args, session);
260
+ break;
261
+ case 'NLST':
262
+ await this.handleNlst(args, session);
263
+ break;
264
+ case 'RETR':
265
+ await this.handleRetr(args, session);
266
+ break;
267
+ case 'STOR':
268
+ await this.handleStor(args, session);
269
+ break;
270
+ case 'SIZE':
271
+ this.handleSize(args, session);
272
+ break;
273
+ case 'MDTM':
274
+ this.handleMdtm(args, session);
275
+ break;
276
+ case 'MKD':
277
+ case 'XMKD':
278
+ this.handleMkd(args, session);
279
+ break;
280
+ case 'RMD':
281
+ case 'XRMD':
282
+ this.handleRmd(args, session);
283
+ break;
284
+ case 'DELE':
285
+ this.handleDele(args, session);
286
+ break;
287
+ case 'NOOP':
288
+ this.sendResponse(session.socket, 200, 'NOOP ok');
289
+ break;
290
+ case 'QUIT':
291
+ this.sendResponse(session.socket, 221, 'Goodbye');
292
+ session.socket.end();
293
+ break;
294
+ default:
295
+ this.sendResponse(session.socket, 502, `Command not implemented: ${command}`);
296
+ }
297
+ }
298
+ handleUser(username, session) {
299
+ session.username = username;
300
+ if (this.options.anonymous && (username.toLowerCase() === 'anonymous' || username.toLowerCase() === 'ftp')) {
301
+ session.authenticated = true;
302
+ this.sendResponse(session.socket, 230, 'Anonymous access granted');
303
+ }
304
+ else {
305
+ this.sendResponse(session.socket, 331, 'Password required');
306
+ }
307
+ }
308
+ handlePass(password, session) {
309
+ if (session.authenticated) {
310
+ this.sendResponse(session.socket, 230, 'Already logged in');
311
+ return;
312
+ }
313
+ if (session.username === this.options.username && password === this.options.password) {
314
+ session.authenticated = true;
315
+ this.sendResponse(session.socket, 230, 'Login successful');
316
+ }
317
+ else if (this.options.anonymous && session.username?.toLowerCase() === 'anonymous') {
318
+ session.authenticated = true;
319
+ this.sendResponse(session.socket, 230, 'Anonymous access granted');
320
+ }
321
+ else {
322
+ this.sendResponse(session.socket, 530, 'Login incorrect');
323
+ }
324
+ }
325
+ handleCwd(path, session) {
326
+ if (!this.requireAuth(session))
327
+ return;
328
+ const newPath = this.resolvePath(path, session.currentDir);
329
+ const dir = this.files.get(newPath);
330
+ if (dir && dir.isDirectory) {
331
+ session.currentDir = newPath;
332
+ this.sendResponse(session.socket, 250, 'Directory changed');
333
+ }
334
+ else {
335
+ this.sendResponse(session.socket, 550, 'Directory not found');
336
+ }
337
+ }
338
+ handleCdup(session) {
339
+ if (!this.requireAuth(session))
340
+ return;
341
+ if (session.currentDir !== '/') {
342
+ const parts = session.currentDir.split('/').filter(Boolean);
343
+ parts.pop();
344
+ session.currentDir = '/' + parts.join('/') || '/';
345
+ }
346
+ this.sendResponse(session.socket, 250, 'Directory changed');
347
+ }
348
+ handleType(type, session) {
349
+ const mode = type.toUpperCase();
350
+ if (mode === 'A' || mode === 'A N') {
351
+ session.transferMode = 'ASCII';
352
+ this.sendResponse(session.socket, 200, 'Type set to ASCII');
353
+ }
354
+ else if (mode === 'I' || mode === 'L 8') {
355
+ session.transferMode = 'BINARY';
356
+ this.sendResponse(session.socket, 200, 'Type set to Binary');
357
+ }
358
+ else {
359
+ this.sendResponse(session.socket, 504, 'Type not supported');
360
+ }
361
+ }
362
+ async handlePasv(session) {
363
+ if (!this.requireAuth(session))
364
+ return;
365
+ session.passiveSocket?.close();
366
+ const dataPort = this.dataPortCounter++;
367
+ if (this.dataPortCounter > 32000)
368
+ this.dataPortCounter = 30000;
369
+ return new Promise((resolve) => {
370
+ session.passiveSocket = net.createServer();
371
+ session.passiveSocket.listen(dataPort, this.options.host, () => {
372
+ const p1 = Math.floor(dataPort / 256);
373
+ const p2 = dataPort % 256;
374
+ const hostParts = this.options.host.split('.');
375
+ this.sendResponse(session.socket, 227, `Entering Passive Mode (${hostParts.join(',')},${p1},${p2})`);
376
+ resolve();
377
+ });
378
+ });
379
+ }
380
+ async handleList(path, session) {
381
+ if (!this.requireAuth(session))
382
+ return;
383
+ const targetPath = path ? this.resolvePath(path, session.currentDir) : session.currentDir;
384
+ const entries = this.listDirectory(targetPath);
385
+ this.sendResponse(session.socket, 150, 'Opening data connection');
386
+ const dataSocket = await this.waitForDataConnection(session);
387
+ if (!dataSocket) {
388
+ this.sendResponse(session.socket, 425, 'No data connection');
389
+ return;
390
+ }
391
+ const lines = [];
392
+ for (const entry of entries) {
393
+ const entryPath = targetPath === '/' ? `/${entry}` : `${targetPath}/${entry}`;
394
+ const file = this.files.get(entryPath);
395
+ if (file) {
396
+ const type = file.isDirectory ? 'd' : '-';
397
+ const size = file.size.toString().padStart(8);
398
+ const date = file.modified.toISOString().substring(0, 10);
399
+ lines.push(`${type}rw-r--r-- 1 user group ${size} ${date} ${entry}`);
400
+ }
401
+ }
402
+ dataSocket.write(lines.join('\r\n') + '\r\n');
403
+ dataSocket.end();
404
+ this.sendResponse(session.socket, 226, 'Transfer complete');
405
+ }
406
+ async handleNlst(path, session) {
407
+ if (!this.requireAuth(session))
408
+ return;
409
+ const targetPath = path ? this.resolvePath(path, session.currentDir) : session.currentDir;
410
+ const entries = this.listDirectory(targetPath);
411
+ this.sendResponse(session.socket, 150, 'Opening data connection');
412
+ const dataSocket = await this.waitForDataConnection(session);
413
+ if (!dataSocket) {
414
+ this.sendResponse(session.socket, 425, 'No data connection');
415
+ return;
416
+ }
417
+ dataSocket.write(entries.join('\r\n') + '\r\n');
418
+ dataSocket.end();
419
+ this.sendResponse(session.socket, 226, 'Transfer complete');
420
+ }
421
+ async handleRetr(filename, session) {
422
+ if (!this.requireAuth(session))
423
+ return;
424
+ const filePath = this.resolvePath(filename, session.currentDir);
425
+ const file = this.files.get(filePath);
426
+ if (!file || file.isDirectory) {
427
+ this.sendResponse(session.socket, 550, 'File not found');
428
+ return;
429
+ }
430
+ this.sendResponse(session.socket, 150, 'Opening data connection');
431
+ const dataSocket = await this.waitForDataConnection(session);
432
+ if (!dataSocket) {
433
+ this.sendResponse(session.socket, 425, 'No data connection');
434
+ return;
435
+ }
436
+ const content = typeof file.content === 'string' ? Buffer.from(file.content) : file.content;
437
+ dataSocket.write(content);
438
+ dataSocket.end();
439
+ this.stats.filesDownloaded++;
440
+ this.stats.bytesTransferred += content.length;
441
+ this.sendResponse(session.socket, 226, 'Transfer complete');
442
+ }
443
+ async handleStor(filename, session) {
444
+ if (!this.requireAuth(session))
445
+ return;
446
+ const filePath = this.resolvePath(filename, session.currentDir);
447
+ this.sendResponse(session.socket, 150, 'Opening data connection');
448
+ const dataSocket = await this.waitForDataConnection(session);
449
+ if (!dataSocket) {
450
+ this.sendResponse(session.socket, 425, 'No data connection');
451
+ return;
452
+ }
453
+ const chunks = [];
454
+ dataSocket.on('data', (chunk) => {
455
+ chunks.push(chunk);
456
+ });
457
+ dataSocket.on('end', () => {
458
+ const content = Buffer.concat(chunks);
459
+ this.addFile(filePath, content);
460
+ this.stats.filesUploaded++;
461
+ this.stats.bytesTransferred += content.length;
462
+ this.sendResponse(session.socket, 226, 'Transfer complete');
463
+ });
464
+ }
465
+ handleSize(filename, session) {
466
+ if (!this.requireAuth(session))
467
+ return;
468
+ const filePath = this.resolvePath(filename, session.currentDir);
469
+ const file = this.files.get(filePath);
470
+ if (!file || file.isDirectory) {
471
+ this.sendResponse(session.socket, 550, 'File not found');
472
+ }
473
+ else {
474
+ this.sendResponse(session.socket, 213, file.size.toString());
475
+ }
476
+ }
477
+ handleMdtm(filename, session) {
478
+ if (!this.requireAuth(session))
479
+ return;
480
+ const filePath = this.resolvePath(filename, session.currentDir);
481
+ const file = this.files.get(filePath);
482
+ if (!file) {
483
+ this.sendResponse(session.socket, 550, 'File not found');
484
+ }
485
+ else {
486
+ const date = file.modified.toISOString().replace(/[-:T]/g, '').substring(0, 14);
487
+ this.sendResponse(session.socket, 213, date);
488
+ }
489
+ }
490
+ handleMkd(dirname, session) {
491
+ if (!this.requireAuth(session))
492
+ return;
493
+ const dirPath = this.resolvePath(dirname, session.currentDir);
494
+ this.addDirectory(dirPath);
495
+ this.sendResponse(session.socket, 257, `"${dirPath}" created`);
496
+ }
497
+ handleRmd(dirname, session) {
498
+ if (!this.requireAuth(session))
499
+ return;
500
+ const dirPath = this.resolvePath(dirname, session.currentDir);
501
+ if (this.removeFile(dirPath)) {
502
+ this.sendResponse(session.socket, 250, 'Directory removed');
503
+ }
504
+ else {
505
+ this.sendResponse(session.socket, 550, 'Directory not found');
506
+ }
507
+ }
508
+ handleDele(filename, session) {
509
+ if (!this.requireAuth(session))
510
+ return;
511
+ const filePath = this.resolvePath(filename, session.currentDir);
512
+ if (this.removeFile(filePath)) {
513
+ this.sendResponse(session.socket, 250, 'File deleted');
514
+ }
515
+ else {
516
+ this.sendResponse(session.socket, 550, 'File not found');
517
+ }
518
+ }
519
+ requireAuth(session) {
520
+ if (!session.authenticated) {
521
+ this.sendResponse(session.socket, 530, 'Please login first');
522
+ return false;
523
+ }
524
+ return true;
525
+ }
526
+ resolvePath(path, currentDir) {
527
+ if (path.startsWith('/')) {
528
+ return this.normalizePath(path);
529
+ }
530
+ return this.normalizePath(currentDir + '/' + path);
531
+ }
532
+ sendResponse(socket, code, message) {
533
+ socket.write(`${code} ${message}\r\n`);
534
+ }
535
+ sendMultilineResponse(socket, code, lines) {
536
+ for (let i = 0; i < lines.length - 1; i++) {
537
+ socket.write(`${code}-${lines[i]}\r\n`);
538
+ }
539
+ socket.write(`${code} ${lines[lines.length - 1]}\r\n`);
540
+ }
541
+ async waitForDataConnection(session) {
542
+ if (!session.passiveSocket)
543
+ return null;
544
+ return new Promise((resolve) => {
545
+ const timeout = setTimeout(() => {
546
+ session.passiveSocket?.close();
547
+ resolve(null);
548
+ }, 5000);
549
+ session.passiveSocket.once('connection', (socket) => {
550
+ clearTimeout(timeout);
551
+ session.passiveSocket.close();
552
+ session.passiveSocket = null;
553
+ resolve(socket);
554
+ });
555
+ });
556
+ }
557
+ static async create(options = {}) {
558
+ const server = new MockFtpServer(options);
559
+ await server.start();
560
+ return server;
561
+ }
562
+ }
@@ -78,4 +78,3 @@ export declare class MockHlsServer extends EventEmitter {
78
78
  export declare function createMockHlsVod(segmentCount?: number, options?: Omit<MockHlsServerOptions, 'mode' | 'segmentCount'>): Promise<MockHlsServer>;
79
79
  export declare function createMockHlsLive(options?: Omit<MockHlsServerOptions, 'mode'>): Promise<MockHlsServer>;
80
80
  export declare function createMockHlsMultiQuality(options?: Omit<MockHlsServerOptions, 'multiQuality'>): Promise<MockHlsServer>;
81
- //# sourceMappingURL=mock-hls-server.d.ts.map
@@ -97,4 +97,3 @@ export declare class MockHttpServer extends EventEmitter {
97
97
  static create(options?: MockHttpServerOptions): Promise<MockHttpServer>;
98
98
  }
99
99
  export declare function createMockHttpServer(routes?: Record<string, MockHttpResponse>, options?: MockHttpServerOptions): Promise<MockHttpServer>;
100
- //# sourceMappingURL=mock-http-server.d.ts.map
@@ -74,4 +74,3 @@ export declare class MockSSEServer extends EventEmitter {
74
74
  static create(options?: MockSSEServerOptions): Promise<MockSSEServer>;
75
75
  }
76
76
  export declare function createMockSSEServer(options?: MockSSEServerOptions): Promise<MockSSEServer>;
77
- //# sourceMappingURL=mock-sse-server.d.ts.map
@@ -0,0 +1,59 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import * as net from 'node:net';
3
+ export interface MockTelnetServerOptions {
4
+ port?: number;
5
+ host?: string;
6
+ echo?: boolean;
7
+ banner?: string;
8
+ prompt?: string;
9
+ delay?: number;
10
+ }
11
+ export type CommandHandler = string | ((args: string[], session: TelnetSession) => string | Promise<string>);
12
+ export interface TelnetSession {
13
+ id: string;
14
+ socket: net.Socket;
15
+ connectedAt: Date;
16
+ lastActivity: Date;
17
+ data: Record<string, unknown>;
18
+ }
19
+ export interface MockTelnetStats {
20
+ connectionsTotal: number;
21
+ connectionsActive: number;
22
+ commandsReceived: number;
23
+ commandLog: Array<{
24
+ command: string;
25
+ sessionId: string;
26
+ timestamp: number;
27
+ }>;
28
+ }
29
+ export declare class MockTelnetServer extends EventEmitter {
30
+ private options;
31
+ private server;
32
+ private sessions;
33
+ private commands;
34
+ private started;
35
+ private sessionCounter;
36
+ private stats;
37
+ constructor(options?: MockTelnetServerOptions);
38
+ get isRunning(): boolean;
39
+ get port(): number;
40
+ get host(): string;
41
+ get url(): string;
42
+ get activeSessions(): number;
43
+ get statistics(): MockTelnetStats;
44
+ addCommand(name: string, handler: CommandHandler): void;
45
+ removeCommand(name: string): void;
46
+ getCommand(name: string): CommandHandler | undefined;
47
+ clearCommands(): void;
48
+ private addDefaultCommands;
49
+ broadcast(message: string): void;
50
+ getSession(id: string): TelnetSession | undefined;
51
+ disconnectSession(id: string): boolean;
52
+ start(): Promise<void>;
53
+ stop(): Promise<void>;
54
+ reset(): void;
55
+ private handleConnection;
56
+ private filterTelnetCommands;
57
+ private handleCommand;
58
+ static create(options?: MockTelnetServerOptions): Promise<MockTelnetServer>;
59
+ }