index.ts 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. /**
  2. * Anonymous public HTTP(S) `WebFetchProvider` plugin. It contributes to the
  3. * `ctx.web` registry without owning the service.
  4. *
  5. * @module @deepseek-ai/dsh-web-fetch-http
  6. */
  7. import type { Context } from '@deepseek-ai/cordis'
  8. import z from '@deepseek-ai/schemastery'
  9. import type {} from '@deepseek-ai/dsh-web'
  10. import { HttpFetchProvider } from './provider.ts'
  11. import type { HttpFetchLimits } from './provider.ts'
  12. const MAX_NODE_TIMER_DELAY_MS = 2_147_483_647
  13. export {
  14. LOCAL_FETCH_PROVIDER_ID,
  15. HttpFetchProvider,
  16. } from './provider.ts'
  17. export type { HttpFetchLimits, HttpFetchResolver } from './provider.ts'
  18. /** Default `User-Agent`: an explicit product agent, never a browser disguise. */
  19. export const DEFAULT_USER_AGENT = 'deepseek-harness/0.0.1 (+https://github.com/deepseek-ai)'
  20. /** Cordis plugin name used by loader diagnostics. */
  21. export const name = 'web-fetch-http'
  22. /** The web seam this provider registers into. */
  23. export const inject = ['web']
  24. /** Plugin config: the provider's transport and size limits plus its `User-Agent` (all defaulted). */
  25. export interface Config {
  26. /** Maximum response body size in bytes. */
  27. maxResponseBytes?: number
  28. /** Maximum decoded body length in characters. */
  29. maxBodyChars?: number
  30. /** Default fetch timeout in milliseconds, within Node's timer range. */
  31. timeoutMs?: number
  32. /** Maximum number of same-origin redirect hops to follow. */
  33. maxRedirects?: number
  34. /** `User-Agent` header sent on every request. */
  35. userAgent?: string
  36. }
  37. export const Config: z<Config> = z.object({
  38. maxResponseBytes: z.number().default(5_000_000),
  39. maxBodyChars: z.number().default(100_000),
  40. timeoutMs: z.number().default(30_000),
  41. maxRedirects: z.number().default(5),
  42. userAgent: z.string().default(DEFAULT_USER_AGENT),
  43. })
  44. /** Complete config after schemastery applies every field default. */
  45. type ResolvedConfig = Required<Config>
  46. /** A resource limit (byte/char/length/timeout cap) must be a positive finite number. */
  47. function assertPositiveFinite(name: string, value: number): void {
  48. if (!Number.isFinite(value) || value <= 0) {
  49. throw new Error(`web-fetch-http: ${name} must be a positive finite number`)
  50. }
  51. }
  52. /** Node coerces larger timer delays to 1 ms, so reject them at configuration time. */
  53. function assertTimeoutMs(value: number): void {
  54. assertPositiveFinite('timeoutMs', value)
  55. if (value > MAX_NODE_TIMER_DELAY_MS) {
  56. throw new Error(`web-fetch-http: timeoutMs must be no greater than ${MAX_NODE_TIMER_DELAY_MS}`)
  57. }
  58. }
  59. /** The redirect hop cap must be a non-negative integer (0 follows no redirects). */
  60. function assertNonNegativeInteger(name: string, value: number): void {
  61. if (!Number.isInteger(value) || value < 0) {
  62. throw new Error(`web-fetch-http: ${name} must be a non-negative integer`)
  63. }
  64. }
  65. /** Register the local HTTP(S) fetch provider with `ctx.web`. */
  66. export function apply(ctx: Context, config: Config): void {
  67. // schemastery (Config) has already filled every defaulted field.
  68. const resolved = config as ResolvedConfig
  69. assertPositiveFinite('maxResponseBytes', resolved.maxResponseBytes)
  70. assertPositiveFinite('maxBodyChars', resolved.maxBodyChars)
  71. assertTimeoutMs(resolved.timeoutMs)
  72. assertNonNegativeInteger('maxRedirects', resolved.maxRedirects)
  73. const limits: HttpFetchLimits = {
  74. maxResponseBytes: resolved.maxResponseBytes,
  75. maxBodyChars: resolved.maxBodyChars,
  76. timeoutMs: resolved.timeoutMs,
  77. maxRedirects: resolved.maxRedirects,
  78. userAgent: resolved.userAgent,
  79. }
  80. ctx.web.registerFetchProvider(new HttpFetchProvider(limits))
  81. }