{"version":3,"sources":["../../../src/adapters/rest/adapter.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * REST API Adapter for GQP\n * Wraps REST APIs as GQP data sources\n */\n\nimport type {\n  GQPAdapter,\n  GQPNode,\n  GQPField,\n  GQPEdge,\n  GQPCapability,\n  FieldType,\n  QueryFilters,\n  QueryOptions,\n} from \"../../core/types.js\";\n\n/**\n * REST resource configuration\n */\nexport interface RESTResource {\n  /** API endpoint path (e.g., '/users' or '/users/:id') */\n  endpoint: string;\n  /** Primary key field name */\n  idField?: string;\n  /** Resource description */\n  description?: string;\n  /** Schema definition for the resource */\n  schema?: Record<string, FieldType | { type: FieldType; nullable?: boolean }>;\n  /** Related resources */\n  relations?: Record<\n    string,\n    { resource: string; type: \"belongsTo\" | \"hasOne\" | \"hasMany\" }\n  >;\n  /** Custom headers for this resource */\n  headers?: Record<string, string>;\n}\n\n/**\n * REST adapter configuration\n */\nexport interface RESTAdapterConfig {\n  /** Base URL for the API */\n  baseURL: string;\n  /** Authentication configuration */\n  auth?: {\n    type: \"bearer\" | \"basic\" | \"apikey\";\n    token?: string;\n    apiKey?: string;\n    apiKeyHeader?: string;\n    username?: string;\n    password?: string;\n  };\n  /** Default headers */\n  headers?: Record<string, string>;\n  /** Resource definitions */\n  resources: Record<string, RESTResource>;\n  /** Rate limiting */\n  rateLimit?: {\n    requests: number;\n    window: \"second\" | \"minute\" | \"hour\";\n  };\n  /** Request timeout in ms */\n  timeout?: number;\n}\n\n/**\n * REST Adapter Implementation\n */\nexport class RESTAdapter implements GQPAdapter {\n  name = \"rest\";\n  private config: RESTAdapterConfig;\n  private rateLimitState: Map<string, { count: number; resetTime: number }> =\n    new Map();\n\n  constructor(config: RESTAdapterConfig) {\n    this.config = config;\n  }\n\n  /**\n   * Introspect REST API resources and return GQP nodes\n   */\n  async introspect(): Promise<GQPNode[]> {\n    const nodes: GQPNode[] = [];\n\n    for (const [resourceName, resource] of Object.entries(\n      this.config.resources,\n    )) {\n      const node = this.resourceToNode(resourceName, resource);\n      nodes.push(node);\n    }\n\n    return nodes;\n  }\n\n  /**\n   * Execute a query against the REST API\n   */\n  async execute(\n    nodeName: string,\n    filters: QueryFilters,\n    options: QueryOptions,\n  ): Promise<unknown[]> {\n    const resource = this.config.resources[nodeName];\n    if (!resource) {\n      throw new Error(`Resource \"${nodeName}\" not found`);\n    }\n\n    // Check rate limit\n    await this.checkRateLimit(nodeName);\n\n    // Build URL with query params\n    const url = this.buildURL(resource.endpoint, filters, options);\n\n    // Make request\n    const response = await this.fetch(url, {\n      method: \"GET\",\n      headers: this.buildHeaders(resource.headers),\n    });\n\n    if (!response.ok) {\n      throw new Error(\n        `REST API error: ${response.status} ${response.statusText}`,\n      );\n    }\n\n    const data = (await response.json()) as any;\n\n    // Handle different response formats\n    if (Array.isArray(data)) {\n      return data;\n    }\n    if (data.data && Array.isArray(data.data)) {\n      return data.data;\n    }\n    if (data.results && Array.isArray(data.results)) {\n      return data.results;\n    }\n    if (data.items && Array.isArray(data.items)) {\n      return data.items;\n    }\n\n    // Single item response\n    return [data];\n  }\n\n  /**\n   * Get count from REST API (if supported)\n   */\n  async count(nodeName: string, filters: QueryFilters): Promise<number> {\n    // Many REST APIs don't support count, so we estimate\n    const results = await this.execute(nodeName, filters, { limit: 1000 });\n    return results.length;\n  }\n\n  // ============================================================================\n  // Private Methods\n  // ============================================================================\n\n  /**\n   * Convert REST resource to GQP node\n   */\n  private resourceToNode(name: string, resource: RESTResource): GQPNode {\n    const fields: GQPField[] = [];\n    const edges: GQPEdge[] = [];\n    const capabilities: GQPCapability[] = [];\n\n    // Build fields from schema\n    if (resource.schema) {\n      for (const [fieldName, fieldDef] of Object.entries(resource.schema)) {\n        const type = typeof fieldDef === \"string\" ? fieldDef : fieldDef.type;\n        const nullable =\n          typeof fieldDef === \"object\" ? (fieldDef.nullable ?? true) : true;\n\n        fields.push({\n          name: fieldName,\n          type,\n          nullable,\n          isList: false,\n          directives: [],\n        });\n\n        // Add capabilities based on type\n        if (type === \"String\") {\n          capabilities.push(\"full_text\");\n        }\n        if (type === \"DateTime\") {\n          capabilities.push(\"temporal_filter\");\n        }\n      }\n    } else {\n      // Add common default fields\n      fields.push({\n        name: resource.idField || \"id\",\n        type: \"ID\",\n        nullable: false,\n        isList: false,\n        directives: [],\n      });\n    }\n\n    // Build edges from relations\n    if (resource.relations) {\n      for (const [relName, rel] of Object.entries(resource.relations)) {\n        edges.push({\n          name: relName,\n          to: rel.resource,\n          relation: rel.type,\n        });\n      }\n    }\n\n    return {\n      name,\n      description: resource.description,\n      fields,\n      edges,\n      capabilities: [...new Set(capabilities)],\n      source: this.name,\n    };\n  }\n\n  /**\n   * Build URL with query parameters - with SSRF protection\n   */\n  private buildURL(\n    endpoint: string,\n    filters: QueryFilters,\n    options: QueryOptions,\n  ): string {\n    const cleanEndpoint = endpoint.split(/[^a-zA-Z0-9/_-]/)[0] || endpoint;\n\n    let url: URL;\n    try {\n      url = new URL(cleanEndpoint, this.config.baseURL);\n    } catch {\n      throw new Error(\"Invalid URL construction\");\n    }\n\n    if (![\"http:\", \"https:\"].includes(url.protocol)) {\n      throw new Error(\"Only HTTP/HTTPS protocols are allowed\");\n    }\n\n    const hostname = url.hostname.toLowerCase();\n    if (this.isPrivateHost(hostname)) {\n      throw new Error(\"Requests to private networks are not allowed\");\n    }\n\n    for (const [key, value] of Object.entries(filters)) {\n      if (value === undefined || value === null) continue;\n\n      if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) continue;\n\n      if (\n        typeof value === \"string\" ||\n        typeof value === \"number\" ||\n        typeof value === \"boolean\"\n      ) {\n        url.searchParams.set(key, String(value));\n      }\n    }\n\n    if (options.limit) {\n      const limit = Math.min(Math.max(1, Number(options.limit) || 10), 100);\n      url.searchParams.set(\"limit\", String(limit));\n    }\n    if (options.offset) {\n      const offset = Math.max(0, Number(options.offset) || 0);\n      url.searchParams.set(\"offset\", String(offset));\n    }\n\n    if (options.orderBy && typeof options.orderBy.field === \"string\") {\n      if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(options.orderBy.field)) {\n        url.searchParams.set(\"sort\", options.orderBy.field);\n        url.searchParams.set(\n          \"order\",\n          options.orderBy.direction === \"desc\" ? \"desc\" : \"asc\",\n        );\n      }\n    }\n\n    return url.toString();\n  }\n\n  /**\n   * Check if hostname is a private/internal network\n   */\n  private isPrivateHost(hostname: string): boolean {\n    if (\n      hostname === \"localhost\" ||\n      hostname === \"127.0.0.1\" ||\n      hostname === \"::1\"\n    ) {\n      return true;\n    }\n\n    if (hostname.startsWith(\"10.\") || hostname.startsWith(\"192.168.\")) {\n      return true;\n    }\n\n    const parts = hostname.split(\".\");\n    if (parts[0] === \"172\") {\n      const second = parseInt(parts[1], 10);\n      if (second >= 16 && second <= 31) return true;\n    }\n\n    if (hostname.endsWith(\".local\") || hostname.endsWith(\".internal\")) {\n      return true;\n    }\n\n    return false;\n  }\n\n  /**\n   * Build request headers - with injection protection\n   */\n  private buildHeaders(\n    resourceHeaders?: Record<string, string>,\n  ): Record<string, string> {\n    const headers: Record<string, string> = {\n      \"Content-Type\": \"application/json\",\n    };\n\n    const safeAdd = (key: string, value: string) => {\n      const cleanKey = key.replace(/[\\r\\n]/g, \"\");\n      const cleanValue = String(value).replace(/[\\r\\n]/g, \"\");\n      if (/^[a-zA-Z0-9-]+$/.test(cleanKey)) {\n        headers[cleanKey] = cleanValue;\n      }\n    };\n\n    if (this.config.headers) {\n      for (const [key, value] of Object.entries(this.config.headers)) {\n        safeAdd(key, value);\n      }\n    }\n\n    if (resourceHeaders) {\n      for (const [key, value] of Object.entries(resourceHeaders)) {\n        safeAdd(key, value);\n      }\n    }\n\n    if (this.config.auth) {\n      switch (this.config.auth.type) {\n        case \"bearer\":\n          if (this.config.auth.token) {\n            headers[\"Authorization\"] =\n              `Bearer ${this.config.auth.token.replace(/[\\r\\n]/g, \"\")}`;\n          }\n          break;\n        case \"basic\":\n          if (this.config.auth.username && this.config.auth.password) {\n            const credentials = btoa(\n              `${this.config.auth.username}:${this.config.auth.password}`,\n            );\n            headers[\"Authorization\"] = `Basic ${credentials}`;\n          }\n          break;\n        case \"apikey\":\n          if (this.config.auth.apiKey) {\n            const headerName = (\n              this.config.auth.apiKeyHeader || \"X-API-Key\"\n            ).replace(/[\\r\\n]/g, \"\");\n            if (/^[a-zA-Z0-9-]+$/.test(headerName)) {\n              headers[headerName] = this.config.auth.apiKey.replace(\n                /[\\r\\n]/g,\n                \"\",\n              );\n            }\n          }\n          break;\n      }\n    }\n\n    return headers;\n  }\n\n  /**\n   * Make HTTP request with timeout\n   */\n  private async fetch(url: string, init: RequestInit): Promise<Response> {\n    const timeout = this.config.timeout || 30000;\n\n    const controller = new AbortController();\n    const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n    try {\n      const response = await fetch(url, {\n        ...init,\n        signal: controller.signal,\n      });\n      return response;\n    } finally {\n      clearTimeout(timeoutId);\n    }\n  }\n\n  /**\n   * Check and update rate limit\n   */\n  private async checkRateLimit(resourceName: string): Promise<void> {\n    if (!this.config.rateLimit) return;\n\n    const key = resourceName;\n    const now = Date.now();\n    const state = this.rateLimitState.get(key);\n\n    // Get window in ms\n    const windowMs =\n      this.config.rateLimit.window === \"second\"\n        ? 1000\n        : this.config.rateLimit.window === \"minute\"\n          ? 60000\n          : 3600000;\n\n    if (!state || now > state.resetTime) {\n      // New window\n      this.rateLimitState.set(key, {\n        count: 1,\n        resetTime: now + windowMs,\n      });\n      return;\n    }\n\n    if (state.count >= this.config.rateLimit.requests) {\n      // Rate limited - wait until reset\n      const waitTime = state.resetTime - now;\n      await new Promise((resolve) => setTimeout(resolve, waitTime));\n      this.rateLimitState.set(key, {\n        count: 1,\n        resetTime: Date.now() + windowMs,\n      });\n      return;\n    }\n\n    // Increment count\n    state.count++;\n  }\n}\n\n/**\n * Factory function to create REST adapter\n *\n * @example\n * ```typescript\n * import { GQP } from '@mzhub/gqp';\n * import { fromREST } from '@mzhub/gqp/rest';\n *\n * const graph = new GQP({\n *   sources: {\n *     stripe: fromREST({\n *       baseURL: 'https://api.stripe.com/v1',\n *       auth: { type: 'bearer', token: process.env.STRIPE_KEY },\n *       resources: {\n *         charges: {\n *           endpoint: '/charges',\n *           idField: 'id',\n *           schema: {\n *             id: 'String',\n *             amount: 'Int',\n *             status: 'String'\n *           }\n *         }\n *       }\n *     })\n *   }\n * });\n * ```\n */\nexport function fromREST(config: RESTAdapterConfig): GQPAdapter {\n  return new RESTAdapter(config);\n}\n"]}