@buun_group/gunspec-sdk 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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/auth.ts","../src/core/errors.ts","../src/core/retry.ts","../src/core/http-client.ts","../src/resources/firearms.ts","../src/resources/manufacturers.ts","../src/resources/calibers.ts","../src/resources/categories.ts","../src/resources/stats.ts","../src/resources/game.ts","../src/resources/game-stats.ts","../src/resources/ammunition.ts","../src/resources/countries.ts","../src/resources/conflicts.ts","../src/resources/data-quality.ts","../src/resources/favorites.ts","../src/resources/reports.ts","../src/resources/support.ts","../src/resources/webhooks.ts","../src/resources/usage.ts","../src/version.ts","../src/client.ts","../src/core/pagination.ts"],"names":["composed"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Authentication handler for the GunSpec SDK.\n *\n * Resolves an API key from an explicit option or the `GUNSPEC_API_KEY`\n * environment variable, and produces the header map needed for every\n * authenticated request.\n *\n * @module\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Options accepted by the authentication handler.\n */\nexport interface AuthConfig {\n /**\n * An explicit API key to use for all requests.\n *\n * When omitted the handler falls back to the `GUNSPEC_API_KEY` environment\n * variable (Node.js / Deno / Bun `process.env`). If neither is available\n * the SDK operates in anonymous mode (no auth header is sent).\n */\n apiKey?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Attempt to read `GUNSPEC_API_KEY` from the runtime environment.\n *\n * Works in Node.js, Deno (with `--allow-env`), Bun, and Cloudflare Workers\n * (when the env variable is bound). Returns `undefined` in environments\n * where `process` or `Deno.env` are unavailable.\n */\nfunction readEnvKey(): string | undefined {\n // Node.js / Bun\n try {\n if (typeof process !== 'undefined' && process.env) {\n return process.env.GUNSPEC_API_KEY;\n }\n } catch {\n // process may be defined but restricted (e.g. browser polyfill).\n }\n\n // Deno\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const g = globalThis as any;\n if (typeof g.Deno !== 'undefined' && g.Deno.env) {\n return (g.Deno.env as { get(key: string): string | undefined }).get('GUNSPEC_API_KEY');\n }\n } catch {\n // Deno.env.get throws if --allow-env is missing.\n }\n\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the API key to use for authentication.\n *\n * Resolution order:\n * 1. Explicit `apiKey` option passed to the constructor.\n * 2. `GUNSPEC_API_KEY` environment variable.\n * 3. `undefined` (anonymous / unauthenticated).\n *\n * @param config - Authentication configuration.\n * @returns The resolved API key, or `undefined` if none is available.\n */\nexport function resolveApiKey(config: AuthConfig): string | undefined {\n if (config.apiKey !== undefined && config.apiKey !== '') {\n return config.apiKey;\n }\n return readEnvKey();\n}\n\n/**\n * Build the authentication headers for a request.\n *\n * If an API key is available it is sent via the `X-API-Key` header.\n * Returns an empty object when no key is available so the request proceeds\n * in anonymous mode.\n *\n * @remarks\n * The key value is **never** logged or serialised to avoid accidental\n * credential leakage.\n *\n * @param apiKey - The resolved API key (may be `undefined`).\n * @returns A header record to merge into the outgoing request.\n */\nexport function buildAuthHeaders(apiKey: string | undefined): Record<string, string> {\n const headers: Record<string, string> = {};\n if (apiKey !== undefined && apiKey !== '') {\n headers['X-API-Key'] = apiKey;\n }\n return headers;\n}\n","/**\n * Error hierarchy for the GunSpec SDK.\n *\n * All SDK errors extend {@link GunSpecError} so consumers can catch them\n * uniformly with a single `catch (e) { if (e instanceof GunSpecError) … }`.\n *\n * HTTP errors returned by the API are mapped to specific subclasses of\n * {@link APIError} via the {@link createAPIError} factory function.\n *\n * @module\n */\n\n// ---------------------------------------------------------------------------\n// Base error\n// ---------------------------------------------------------------------------\n\n/**\n * Base error class for every error thrown by the GunSpec SDK.\n *\n * @remarks\n * Restores the prototype chain so `instanceof` checks work correctly even\n * when transpiled to ES5.\n */\nexport class GunSpecError extends Error {\n override readonly name: string = 'GunSpecError';\n\n constructor(message: string) {\n super(message);\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n// ---------------------------------------------------------------------------\n// API errors (HTTP responses with non-2xx status)\n// ---------------------------------------------------------------------------\n\n/**\n * An error returned by the GunSpec API with an HTTP status code.\n *\n * Subclasses are created for well-known status codes (401, 403, 404, …).\n * For any other status code the base `APIError` is thrown directly.\n */\nexport class APIError extends GunSpecError {\n override readonly name: string = 'APIError';\n\n /** HTTP status code returned by the API. */\n readonly status: number;\n\n /** Machine-readable error code from the response body (e.g. `\"NOT_FOUND\"`). */\n readonly code: string;\n\n /** The `X-Request-Id` header value, useful for support requests. */\n readonly requestId: string;\n\n /** Raw response headers for further inspection. */\n readonly headers: Headers;\n\n constructor(\n status: number,\n code: string,\n message: string,\n requestId: string,\n headers: Headers,\n ) {\n super(message);\n this.status = status;\n this.code = code;\n this.requestId = requestId;\n this.headers = headers;\n }\n}\n\n/**\n * Thrown when the API returns **401 Unauthorized**.\n *\n * Usually means the API key is missing, invalid, or expired.\n */\nexport class AuthenticationError extends APIError {\n override readonly name: string = 'AuthenticationError';\n\n constructor(code: string, message: string, requestId: string, headers: Headers) {\n super(401, code, message, requestId, headers);\n }\n}\n\n/**\n * Thrown when the API returns **403 Forbidden**.\n *\n * The API key is valid but the associated tier does not have access to the\n * requested resource or action.\n */\nexport class PermissionError extends APIError {\n override readonly name: string = 'PermissionError';\n\n constructor(code: string, message: string, requestId: string, headers: Headers) {\n super(403, code, message, requestId, headers);\n }\n}\n\n/**\n * Thrown when the API returns **404 Not Found**.\n */\nexport class NotFoundError extends APIError {\n override readonly name: string = 'NotFoundError';\n\n constructor(code: string, message: string, requestId: string, headers: Headers) {\n super(404, code, message, requestId, headers);\n }\n}\n\n/**\n * Thrown when the API returns **400 Bad Request**.\n *\n * Typically caused by invalid query parameters or a malformed request body.\n */\nexport class BadRequestError extends APIError {\n override readonly name: string = 'BadRequestError';\n\n constructor(code: string, message: string, requestId: string, headers: Headers) {\n super(400, code, message, requestId, headers);\n }\n}\n\n/**\n * Thrown when the API returns **429 Too Many Requests**.\n *\n * The {@link retryAfter} property contains the number of seconds to wait\n * before retrying, parsed from the `Retry-After` response header.\n */\nexport class RateLimitError extends APIError {\n override readonly name: string = 'RateLimitError';\n\n /**\n * Number of seconds the client should wait before retrying, or `null` if\n * the server did not provide a `Retry-After` header.\n */\n readonly retryAfter: number | null;\n\n constructor(\n code: string,\n message: string,\n requestId: string,\n headers: Headers,\n retryAfter: number | null,\n ) {\n super(429, code, message, requestId, headers);\n this.retryAfter = retryAfter;\n }\n}\n\n/**\n * Thrown when the API returns **500 Internal Server Error**.\n */\nexport class InternalServerError extends APIError {\n override readonly name: string = 'InternalServerError';\n\n constructor(code: string, message: string, requestId: string, headers: Headers) {\n super(500, code, message, requestId, headers);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Network / transport errors\n// ---------------------------------------------------------------------------\n\n/**\n * Thrown when a network-level failure prevents the request from completing.\n *\n * This covers DNS resolution errors, connection resets, and any other\n * `TypeError` thrown by the native `fetch` implementation.\n */\nexport class ConnectionError extends GunSpecError {\n override readonly name: string = 'ConnectionError';\n\n /** The original error thrown by `fetch`. */\n readonly cause: unknown;\n\n constructor(message: string, cause: unknown) {\n super(message);\n this.cause = cause;\n }\n}\n\n/**\n * Thrown when a request exceeds the configured timeout.\n *\n * The timeout is implemented via `AbortController` and fires when the\n * elapsed time exceeds `HttpClientConfig.timeout`.\n */\nexport class TimeoutError extends GunSpecError {\n override readonly name: string = 'TimeoutError';\n\n /** The timeout duration in milliseconds that was exceeded. */\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Request timed out after ${timeoutMs}ms`);\n this.timeoutMs = timeoutMs;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Parse the `Retry-After` header value into seconds.\n *\n * Handles both delta-seconds (`120`) and HTTP-date formats\n * (`Wed, 21 Oct 2015 07:28:00 GMT`).\n *\n * @returns The number of seconds to wait, or `null` if the header is absent\n * or unparseable.\n */\nfunction parseRetryAfter(headers: Headers): number | null {\n const raw = headers.get('Retry-After');\n if (raw === null) return null;\n\n // Try delta-seconds first (most common for APIs).\n const seconds = Number(raw);\n if (!Number.isNaN(seconds) && seconds >= 0) return seconds;\n\n // Fall back to HTTP-date.\n const date = Date.parse(raw);\n if (!Number.isNaN(date)) {\n const delta = Math.ceil((date - Date.now()) / 1000);\n return delta > 0 ? delta : 0;\n }\n\n return null;\n}\n\n/**\n * Error envelope returned by the GunSpec API on non-2xx responses.\n */\ninterface ErrorBody {\n success: false;\n error: {\n code: string;\n message: string;\n };\n}\n\n/**\n * Create the appropriate {@link APIError} subclass for the given HTTP status\n * code and response body.\n *\n * @param status - HTTP status code.\n * @param body - Parsed JSON body (may be `null` if the body was empty or\n * unparseable).\n * @param requestId - Value of the `X-Request-Id` response header.\n * @param headers - Raw response headers.\n * @returns A concrete {@link APIError} subclass instance.\n */\nexport function createAPIError(\n status: number,\n body: ErrorBody | null,\n requestId: string,\n headers: Headers,\n): APIError {\n const code = body?.error?.code ?? `HTTP_${status}`;\n const message = body?.error?.message ?? `Request failed with status ${status}`;\n\n switch (status) {\n case 400:\n return new BadRequestError(code, message, requestId, headers);\n case 401:\n return new AuthenticationError(code, message, requestId, headers);\n case 403:\n return new PermissionError(code, message, requestId, headers);\n case 404:\n return new NotFoundError(code, message, requestId, headers);\n case 429:\n return new RateLimitError(code, message, requestId, headers, parseRetryAfter(headers));\n case 500:\n return new InternalServerError(code, message, requestId, headers);\n default:\n return new APIError(status, code, message, requestId, headers);\n }\n}\n","/**\n * Retry logic with exponential backoff for the GunSpec SDK.\n *\n * Only **idempotent** HTTP methods (`GET`, `PUT`, `DELETE`) are retried\n * automatically. Transient failures (timeouts, 429, 5xx) trigger a retry\n * after an exponentially increasing delay with +-20 % jitter.\n *\n * @module\n */\n\nimport { APIError, RateLimitError, TimeoutError, ConnectionError } from './errors.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Configuration for the retry behaviour.\n *\n * All fields are optional and fall back to sensible defaults.\n */\nexport interface RetryConfig {\n /**\n * Maximum number of retry attempts (excluding the initial request).\n *\n * Set to `0` to disable retries entirely.\n *\n * @defaultValue `2`\n */\n maxRetries?: number;\n\n /**\n * Initial delay in milliseconds before the first retry.\n *\n * @defaultValue `500`\n */\n initialDelayMs?: number;\n\n /**\n * Upper-bound delay in milliseconds. The computed delay is capped at this\n * value regardless of how many retries have occurred.\n *\n * @defaultValue `8000`\n */\n maxDelayMs?: number;\n\n /**\n * Multiplier applied to the delay after each retry.\n *\n * @defaultValue `2`\n */\n multiplier?: number;\n}\n\n/** Fully resolved retry configuration with all defaults applied. */\nexport interface ResolvedRetryConfig {\n readonly maxRetries: number;\n readonly initialDelayMs: number;\n readonly maxDelayMs: number;\n readonly multiplier: number;\n}\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\n/** Default retry configuration values. */\nconst DEFAULTS: ResolvedRetryConfig = {\n maxRetries: 2,\n initialDelayMs: 500,\n maxDelayMs: 8_000,\n multiplier: 2,\n} as const;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** HTTP methods that are safe to retry automatically. */\nconst IDEMPOTENT_METHODS: ReadonlySet<string> = new Set(['GET', 'PUT', 'DELETE']);\n\n/**\n * HTTP status codes that indicate a transient failure eligible for retry.\n *\n * - 408 Request Timeout\n * - 429 Too Many Requests\n * - 500 Internal Server Error\n * - 502 Bad Gateway\n * - 503 Service Unavailable\n * - 504 Gateway Timeout\n */\nconst RETRYABLE_STATUS_CODES: ReadonlySet<number> = new Set([408, 429, 500, 502, 503, 504]);\n\n/**\n * Resolve a partial {@link RetryConfig} into a fully populated\n * {@link ResolvedRetryConfig} by filling in defaults.\n */\nexport function resolveRetryConfig(config?: RetryConfig): ResolvedRetryConfig {\n return {\n maxRetries: config?.maxRetries ?? DEFAULTS.maxRetries,\n initialDelayMs: config?.initialDelayMs ?? DEFAULTS.initialDelayMs,\n maxDelayMs: config?.maxDelayMs ?? DEFAULTS.maxDelayMs,\n multiplier: config?.multiplier ?? DEFAULTS.multiplier,\n };\n}\n\n/**\n * Determine whether a given error is eligible for an automatic retry.\n *\n * @param error - The error thrown by the HTTP layer.\n * @param method - The HTTP method of the failed request.\n * @returns `true` if the request should be retried.\n */\nexport function isRetryable(error: unknown, method: string): boolean {\n // Only retry idempotent methods to avoid duplicate side-effects.\n if (!IDEMPOTENT_METHODS.has(method.toUpperCase())) {\n return false;\n }\n\n // Network failures and timeouts are always retryable.\n if (error instanceof ConnectionError || error instanceof TimeoutError) {\n return true;\n }\n\n // API errors with specific transient status codes.\n if (error instanceof APIError) {\n return RETRYABLE_STATUS_CODES.has(error.status);\n }\n\n return false;\n}\n\n/**\n * Compute the delay in milliseconds before the next retry attempt.\n *\n * Uses exponential backoff with +-20 % jitter, capped at\n * {@link ResolvedRetryConfig.maxDelayMs}.\n *\n * For {@link RateLimitError} responses that carry a `Retry-After` header the\n * returned delay is the greater of the computed backoff and the server-\n * requested wait time.\n *\n * @param attempt - Zero-based retry attempt index (0 = first retry).\n * @param config - Resolved retry configuration.\n * @param error - The error that triggered the retry (used to read\n * `retryAfter` on 429 responses).\n * @returns Delay in milliseconds.\n */\nexport function computeDelay(\n attempt: number,\n config: ResolvedRetryConfig,\n error: unknown,\n): number {\n // Base exponential delay.\n const base = config.initialDelayMs * Math.pow(config.multiplier, attempt);\n\n // Apply +-20 % jitter.\n const jitter = 1 + (Math.random() * 0.4 - 0.2);\n let delay = Math.min(base * jitter, config.maxDelayMs);\n\n // Honour Retry-After on 429 responses.\n if (error instanceof RateLimitError && error.retryAfter !== null) {\n const retryAfterMs = error.retryAfter * 1000;\n delay = Math.max(delay, retryAfterMs);\n }\n\n return Math.round(delay);\n}\n\n/**\n * Wait for the specified number of milliseconds.\n *\n * The delay is abortable via an `AbortSignal` so that cancellation propagates\n * cleanly to in-flight retries.\n *\n * @param ms - Duration to wait.\n * @param signal - Optional abort signal.\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n return;\n }\n\n const timer = setTimeout(resolve, ms);\n\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n },\n { once: true },\n );\n });\n}\n\n/**\n * Execute an async function with automatic retries on transient failures.\n *\n * @typeParam T - The return type of the function being retried.\n * @param fn - The function to execute (and potentially retry).\n * @param method - The HTTP method, used to determine idempotency.\n * @param config - Retry configuration.\n * @param signal - Optional abort signal to cancel pending retries.\n * @returns The result of a successful invocation of `fn`.\n * @throws The last error encountered when all retry attempts are exhausted.\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n method: string,\n config: ResolvedRetryConfig,\n signal?: AbortSignal,\n): Promise<T> {\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= config.maxRetries; attempt++) {\n try {\n return await fn();\n } catch (error: unknown) {\n lastError = error;\n\n // Don't retry if this is the last attempt or the error isn't retryable.\n const isLastAttempt = attempt === config.maxRetries;\n if (isLastAttempt || !isRetryable(error, method)) {\n throw error;\n }\n\n const delay = computeDelay(attempt, config, error);\n await sleep(delay, signal);\n }\n }\n\n // Unreachable in practice, but satisfies the type checker.\n throw lastError;\n}\n","/**\n * HTTP transport layer for the GunSpec SDK.\n *\n * Uses the native `fetch` API (available in Node 18+, Deno, Bun, Cloudflare\n * Workers, and modern browsers) with zero external dependencies.\n *\n * @module\n */\n\nimport { resolveApiKey, buildAuthHeaders, type AuthConfig } from './auth.js';\nimport {\n createAPIError,\n ConnectionError,\n TimeoutError,\n type APIError,\n} from './errors.js';\nimport {\n resolveRetryConfig,\n withRetry,\n type RetryConfig,\n type ResolvedRetryConfig,\n} from './retry.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Configuration accepted by the {@link HttpClient} constructor.\n */\nexport interface HttpClientConfig {\n /**\n * Base URL for all API requests.\n *\n * Trailing slashes are stripped automatically.\n *\n * @defaultValue `\"https://api.gunspec.io\"`\n */\n baseUrl?: string;\n\n /**\n * Default request timeout in milliseconds.\n *\n * Individual requests can override this via {@link RequestConfig.timeout}.\n *\n * @defaultValue `30_000` (30 seconds)\n */\n timeout?: number;\n\n /**\n * Extra headers merged into every outgoing request.\n *\n * Useful for setting a custom `User-Agent` or forwarding correlation IDs.\n */\n headers?: Record<string, string>;\n\n /** Authentication options. */\n auth?: AuthConfig;\n\n /** Retry / backoff options. */\n retry?: RetryConfig;\n}\n\n/**\n * Describes a single HTTP request to be executed by the client.\n */\nexport interface RequestConfig {\n /** HTTP method. */\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n\n /**\n * URL path **relative to the base URL**.\n *\n * Must start with `/` (e.g. `/v1/firearms`).\n */\n path: string;\n\n /**\n * Query string parameters.\n *\n * - `undefined` values are silently omitted.\n * - Arrays are serialised as repeated keys (`caliber=9mm&caliber=.45`).\n * - Booleans are stringified (`\"true\"` / `\"false\"`).\n */\n query?: Record<string, string | number | boolean | string[] | undefined>;\n\n /** JSON request body (automatically stringified). */\n body?: unknown;\n\n /** Per-request header overrides. */\n headers?: Record<string, string>;\n\n /**\n * Per-request timeout in milliseconds.\n *\n * Overrides the client-level default.\n */\n timeout?: number;\n\n /**\n * An external `AbortSignal` that the caller can use to cancel the request.\n *\n * This signal is composed with the internal timeout signal so that either\n * mechanism can abort the request.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Unwrapped API response for endpoints that return a single resource.\n *\n * The SDK strips the `{ success, data }` envelope and hoists `data` to the\n * top level.\n */\nexport interface APIResponse<T> {\n /** The unwrapped response payload. */\n readonly data: T;\n\n /** HTTP status code. */\n readonly status: number;\n\n /** Raw response headers. */\n readonly headers: Headers;\n\n /** The `X-Request-Id` response header. */\n readonly requestId: string;\n\n /** Parsed rate limit headers. */\n readonly rateLimit: RateLimitInfo;\n}\n\n/**\n * Unwrapped API response for endpoints that return a paginated list.\n */\nexport interface PaginatedResponse<T> {\n /** Array of resource objects. */\n readonly data: T[];\n\n /** Pagination metadata returned by the server. */\n readonly pagination: PaginationMeta;\n\n /** HTTP status code. */\n readonly status: number;\n\n /** Raw response headers. */\n readonly headers: Headers;\n\n /** The `X-Request-Id` response header. */\n readonly requestId: string;\n\n /** Parsed rate limit headers. */\n readonly rateLimit: RateLimitInfo;\n}\n\n/**\n * Pagination metadata included in list responses.\n */\nexport interface PaginationMeta {\n /** Current page number (1-indexed). */\n readonly page: number;\n\n /** Maximum number of items per page. */\n readonly limit: number;\n\n /** Total number of matching items (omitted for anonymous/free tiers). */\n readonly total?: number;\n\n /** Total number of pages (omitted when `total` is omitted). */\n readonly totalPages?: number;\n}\n\n/**\n * Rate limit information parsed from response headers.\n */\nexport interface RateLimitInfo {\n /** Maximum requests allowed per window, or `null` if the header is absent. */\n readonly limit: number | null;\n\n /** Requests remaining in the current window, or `null`. */\n readonly remaining: number | null;\n\n /** Unix timestamp (seconds) when the window resets, or `null`. */\n readonly reset: number | null;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_BASE_URL = 'https://api.gunspec.io';\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst SDK_USER_AGENT = '@gunspec/sdk';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Serialise a query parameter map into a URL search string.\n *\n * - `undefined` values are skipped.\n * - Arrays produce repeated keys (`a=1&a=2`).\n * - Booleans are converted to `\"true\"` / `\"false\"`.\n */\nfunction serialiseQuery(\n params: Record<string, string | number | boolean | string[] | undefined>,\n): string {\n const parts: string[] = [];\n\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue;\n\n if (Array.isArray(value)) {\n for (const item of value) {\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(item)}`);\n }\n } else {\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);\n }\n }\n\n return parts.length > 0 ? `?${parts.join('&')}` : '';\n}\n\n/**\n * Parse rate limit headers from a `Headers` object.\n */\nfunction parseRateLimitHeaders(headers: Headers): RateLimitInfo {\n const parse = (name: string): number | null => {\n const raw = headers.get(name);\n if (raw === null) return null;\n const num = Number(raw);\n return Number.isNaN(num) ? null : num;\n };\n\n return {\n limit: parse('X-RateLimit-Limit'),\n remaining: parse('X-RateLimit-Remaining'),\n reset: parse('X-RateLimit-Reset'),\n };\n}\n\n/**\n * Extract the request ID from response headers.\n */\nfunction extractRequestId(headers: Headers): string {\n return headers.get('X-Request-Id') ?? '';\n}\n\n/**\n * Compose two optional `AbortSignal`s into a single signal that aborts when\n * **either** source fires.\n *\n * Uses `AbortSignal.any()` when available (Node 20+, modern browsers) and\n * falls back to a manual composition for older runtimes.\n */\nfunction composeSignals(\n timeoutMs: number,\n externalSignal?: AbortSignal,\n): { signal: AbortSignal; cleanup: () => void } {\n const timeoutController = new AbortController();\n const timer = setTimeout(() => timeoutController.abort(new TimeoutError(timeoutMs)), timeoutMs);\n\n // If AbortSignal.any is available, use it for clean composition.\n if (typeof AbortSignal !== 'undefined' && 'any' in AbortSignal) {\n const signals: AbortSignal[] = [timeoutController.signal];\n if (externalSignal) signals.push(externalSignal);\n const composed = (AbortSignal as { any: (signals: AbortSignal[]) => AbortSignal }).any(signals);\n return {\n signal: composed,\n cleanup: () => clearTimeout(timer),\n };\n }\n\n // Fallback: manual composition via a new AbortController.\n const composed = new AbortController();\n\n const onTimeoutAbort = () => composed.abort(timeoutController.signal.reason);\n const onExternalAbort = () => {\n if (externalSignal) composed.abort(externalSignal.reason);\n };\n\n timeoutController.signal.addEventListener('abort', onTimeoutAbort, { once: true });\n externalSignal?.addEventListener('abort', onExternalAbort, { once: true });\n\n return {\n signal: composed.signal,\n cleanup: () => {\n clearTimeout(timer);\n timeoutController.signal.removeEventListener('abort', onTimeoutAbort);\n externalSignal?.removeEventListener('abort', onExternalAbort);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// HttpClient\n// ---------------------------------------------------------------------------\n\n/**\n * Low-level HTTP client for the GunSpec API.\n *\n * Handles URL construction, query string serialisation, authentication\n * headers, timeout management, response envelope unwrapping, error mapping,\n * and automatic retries with exponential backoff.\n *\n * Most consumers should use the high-level `GunSpec` client class instead,\n * which delegates to an `HttpClient` instance internally.\n *\n * @example\n * ```ts\n * const http = new HttpClient({ auth: { apiKey: process.env.GUNSPEC_API_KEY } });\n * const res = await http.request<Firearm>({\n * method: 'GET',\n * path: '/v1/firearms/glock-g17',\n * });\n * console.log(res.data.name); // \"Glock G17\"\n * ```\n */\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly defaultTimeout: number;\n private readonly defaultHeaders: Record<string, string>;\n private readonly apiKey: string | undefined;\n private readonly retryConfig: ResolvedRetryConfig;\n\n constructor(config: HttpClientConfig = {}) {\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.defaultTimeout = config.timeout ?? DEFAULT_TIMEOUT_MS;\n this.apiKey = resolveApiKey(config.auth ?? {});\n this.retryConfig = resolveRetryConfig(config.retry);\n\n this.defaultHeaders = {\n 'Accept': 'application/json',\n 'User-Agent': SDK_USER_AGENT,\n ...config.headers,\n };\n }\n\n /**\n * Execute an HTTP request and return the unwrapped response.\n *\n * The API's `{ success: true, data: T }` envelope is stripped so that\n * callers receive `T` directly via {@link APIResponse.data}.\n *\n * @typeParam T - The expected type of the `data` field in the response.\n * @param config - Request configuration.\n * @returns The unwrapped API response.\n * @throws {@link APIError} on non-2xx responses.\n * @throws {@link ConnectionError} on network failures.\n * @throws {@link TimeoutError} when the request exceeds the timeout.\n */\n async request<T>(config: RequestConfig): Promise<APIResponse<T>> {\n return withRetry(\n () => this.executeRequest<T>(config),\n config.method,\n this.retryConfig,\n config.signal,\n );\n }\n\n /**\n * Execute an HTTP request and return a paginated response.\n *\n * The API's `{ success: true, data: T[], pagination }` envelope is\n * unwrapped into a {@link PaginatedResponse}.\n *\n * @typeParam T - The element type of the paginated list.\n * @param config - Request configuration.\n * @returns The unwrapped paginated response.\n * @throws {@link APIError} on non-2xx responses.\n * @throws {@link ConnectionError} on network failures.\n * @throws {@link TimeoutError} when the request exceeds the timeout.\n */\n async requestPaginated<T>(config: RequestConfig): Promise<PaginatedResponse<T>> {\n return withRetry(\n () => this.executePaginatedRequest<T>(config),\n config.method,\n this.retryConfig,\n config.signal,\n );\n }\n\n /**\n * Convenience wrapper for a GET request returning a single resource.\n */\n async get<T>(path: string, query?: Record<string, any>): Promise<APIResponse<T>> {\n return this.request<T>({ method: 'GET', path, query });\n }\n\n /**\n * Convenience wrapper for a GET request returning a paginated list.\n */\n async getPaginated<T>(path: string, query?: Record<string, any>): Promise<PaginatedResponse<T>> {\n return this.requestPaginated<T>({ method: 'GET', path, query });\n }\n\n /**\n * Convenience wrapper for a POST request.\n */\n async post<T>(path: string, body?: unknown, query?: Record<string, any>): Promise<APIResponse<T>> {\n return this.request<T>({ method: 'POST', path, body, query });\n }\n\n /**\n * Convenience wrapper for a PUT request.\n */\n async put<T>(path: string, body?: unknown, query?: Record<string, any>): Promise<APIResponse<T>> {\n return this.request<T>({ method: 'PUT', path, body, query });\n }\n\n /**\n * Convenience wrapper for a DELETE request.\n */\n async delete<T>(path: string, query?: Record<string, any>): Promise<APIResponse<T>> {\n return this.request<T>({ method: 'DELETE', path, query });\n }\n\n // -----------------------------------------------------------------------\n // Internal execution\n // -----------------------------------------------------------------------\n\n private async executeRequest<T>(config: RequestConfig): Promise<APIResponse<T>> {\n const response = await this.doFetch(config);\n const requestId = extractRequestId(response.headers);\n const rateLimit = parseRateLimitHeaders(response.headers);\n\n if (!response.ok) {\n await this.throwAPIError(response, requestId);\n }\n\n const json = (await response.json()) as { success: boolean; data: T };\n\n return {\n data: json.data,\n status: response.status,\n headers: response.headers,\n requestId,\n rateLimit,\n };\n }\n\n private async executePaginatedRequest<T>(config: RequestConfig): Promise<PaginatedResponse<T>> {\n const response = await this.doFetch(config);\n const requestId = extractRequestId(response.headers);\n const rateLimit = parseRateLimitHeaders(response.headers);\n\n if (!response.ok) {\n await this.throwAPIError(response, requestId);\n }\n\n const json = (await response.json()) as {\n success: boolean;\n data: T[];\n pagination: PaginationMeta;\n };\n\n return {\n data: json.data,\n pagination: json.pagination,\n status: response.status,\n headers: response.headers,\n requestId,\n rateLimit,\n };\n }\n\n /**\n * Perform the raw `fetch` call with merged headers, query string, timeout,\n * and body serialisation.\n */\n private async doFetch(config: RequestConfig): Promise<Response> {\n const url = this.buildUrl(config.path, config.query);\n const timeoutMs = config.timeout ?? this.defaultTimeout;\n const { signal, cleanup } = composeSignals(timeoutMs, config.signal);\n\n const headers: Record<string, string> = {\n ...this.defaultHeaders,\n ...buildAuthHeaders(this.apiKey),\n ...config.headers,\n };\n\n if (config.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n\n try {\n const response = await fetch(url, {\n method: config.method,\n headers,\n body: config.body !== undefined ? JSON.stringify(config.body) : undefined,\n signal,\n });\n return response;\n } catch (error: unknown) {\n // Distinguish timeouts from generic network failures.\n if (error instanceof TimeoutError) {\n throw error;\n }\n if (error instanceof DOMException && error.name === 'AbortError') {\n // Check if it was our timeout or the external signal.\n if (config.signal?.aborted) {\n throw error; // Caller-initiated abort — re-throw as-is.\n }\n throw new TimeoutError(timeoutMs);\n }\n throw new ConnectionError(\n error instanceof Error ? error.message : 'Network request failed',\n error,\n );\n } finally {\n cleanup();\n }\n }\n\n /**\n * Build the full URL from base URL, path, and query parameters.\n */\n private buildUrl(\n path: string,\n query?: Record<string, string | number | boolean | string[] | undefined>,\n ): string {\n const qs = query ? serialiseQuery(query) : '';\n return `${this.baseUrl}${path}${qs}`;\n }\n\n /**\n * Parse an error response body and throw the appropriate {@link APIError}.\n */\n private async throwAPIError(response: Response, requestId: string): Promise<never> {\n let body: { success: false; error: { code: string; message: string } } | null = null;\n\n try {\n body = (await response.json()) as {\n success: false;\n error: { code: string; message: string };\n };\n } catch {\n // Body may be empty or not JSON — proceed with null body.\n }\n\n throw createAPIError(response.status, body, requestId, response.headers);\n }\n}\n","/**\n * Firearms resource for the GunSpec SDK.\n *\n * Provides access to all `/v1/firearms` endpoints including listing,\n * searching, comparing, filtering, and retrieving individual firearm\n * details with sub-resources (images, variants, game stats, etc.).\n *\n * @module\n */\n\nimport type { HttpClient, APIResponse, PaginatedResponse } from '../core';\nimport type {\n Firearm,\n FirearmComparison,\n FirearmImage,\n FirearmCalculation,\n FirearmLoadProfile,\n GameStats,\n GameMetaItem,\n ActionTypeStats,\n Dimensions,\n FirearmUser,\n FamilyTree,\n SimilarFirearm,\n AdoptionMap,\n GameProfile,\n Silhouette,\n PowerRating,\n HeadToHead,\n FilterOptions,\n ListFirearmsParams,\n SearchFirearmsParams,\n CompareFirearmsParams,\n GameMetaParams,\n RandomFirearmParams,\n TopFirearmsParams,\n HeadToHeadParams,\n ByFeatureParams,\n ByActionParams,\n ByMaterialParams,\n ByDesignerParams,\n PowerRatingParams,\n TimelineParams,\n ByConflictParams,\n SilhouetteParams,\n CalculateBallisticsParams,\n LoadFirearmParams,\n} from '../types';\n\n/**\n * Resource class for interacting with the GunSpec Firearms API.\n *\n * Wraps all `/v1/firearms` endpoints. Instantiated internally by the\n * {@link GunSpec} client and exposed as `client.firearms`.\n *\n * @example\n * ```typescript\n * import GunSpec from '@gunspec/sdk';\n *\n * const client = new GunSpec();\n *\n * // List firearms with filters\n * const { data, pagination } = await client.firearms.list({\n * manufacturer: 'glock',\n * category: 'pistol',\n * per_page: 10,\n * });\n *\n * // Get a single firearm by slug\n * const { data: firearm } = await client.firearms.get('glock-g17');\n * ```\n */\nexport class FirearmsResource {\n constructor(private readonly client: HttpClient) {}\n\n // ---------------------------------------------------------------------------\n // Collection endpoints\n // ---------------------------------------------------------------------------\n\n /**\n * List firearms with optional filters and pagination.\n *\n * @param params - Optional query parameters for filtering, sorting, and pagination.\n * @returns A paginated list of firearms matching the given filters.\n * @throws {BadRequestError} If any filter value is invalid.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.firearms.list({\n * manufacturer: 'beretta',\n * status: 'in_production',\n * sort: 'name',\n * order: 'asc',\n * page: 1,\n * per_page: 25,\n * });\n * console.log(result.data); // Firearm[]\n * console.log(result.pagination.totalPages);\n * ```\n */\n async list(params?: ListFirearmsParams): Promise<PaginatedResponse<Firearm>> {\n return this.client.getPaginated<Firearm>('/v1/firearms', params as Record<string, any>);\n }\n\n /**\n * Auto-paginate through all firearms matching the given filters.\n *\n * Returns an async iterator that fetches pages on demand, yielding\n * individual {@link Firearm} objects. Useful for processing large\n * result sets without managing pagination manually.\n *\n * @param params - Optional query parameters for filtering and sorting.\n * @returns An async iterable iterator yielding individual firearms.\n *\n * @example\n * ```typescript\n * for await (const firearm of client.firearms.listAutoPaging({ manufacturer: 'colt' })) {\n * console.log(firearm.name);\n * }\n * ```\n */\n async *listAutoPaging(params?: ListFirearmsParams): AsyncIterableIterator<Firearm> {\n let page = params?.page ?? 1;\n while (true) {\n const result = await this.list({ ...params, page });\n for (const item of result.data) {\n yield item;\n }\n if (!result.pagination.totalPages || page >= result.pagination.totalPages) break;\n page++;\n }\n }\n\n /**\n * Full-text search across firearms.\n *\n * @param params - Search query and pagination parameters.\n * @returns A paginated list of firearms matching the search query.\n * @throws {BadRequestError} If the search query is empty or too long.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.firearms.search({ q: '9mm compact' });\n * console.log(result.data.length);\n * ```\n */\n async search(params: SearchFirearmsParams): Promise<PaginatedResponse<Firearm>> {\n return this.client.getPaginated<Firearm>('/v1/firearms/search', params as Record<string, any>);\n }\n\n /**\n * Compare up to 5 firearms side by side.\n *\n * @param params - Object containing comma-separated firearm IDs.\n * @returns An array of firearms with full details for comparison.\n * @throws {BadRequestError} If more than 5 IDs are provided or any ID is invalid.\n * @throws {NotFoundError} If any of the specified firearms do not exist.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.compare({ ids: 'glock-g17,sig-sauer-p320,beretta-92fs' });\n * console.log(data.items.length); // 3\n * ```\n */\n async compare(params: CompareFirearmsParams): Promise<APIResponse<FirearmComparison>> {\n return this.client.get<FirearmComparison>('/v1/firearms/compare', params as Record<string, any>);\n }\n\n /**\n * Retrieve game metadata for firearms (archetypes, stat ranges, etc.).\n *\n * @param params - Optional archetype filter.\n * @returns Game metadata including archetype definitions and stat ranges.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.gameMeta({ archetype: 'sniper' });\n * ```\n */\n async gameMeta(params?: GameMetaParams): Promise<APIResponse<GameMetaItem[]>> {\n return this.client.get<GameMetaItem[]>('/v1/firearms/game-meta', params as Record<string, any>);\n }\n\n /**\n * List all known action types across the database.\n *\n * @returns An array of distinct action type strings.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.actionTypes();\n * // ['Semi-automatic', 'Bolt action', 'Lever action', ...]\n * ```\n */\n async actionTypes(): Promise<APIResponse<ActionTypeStats[]>> {\n return this.client.get<ActionTypeStats[]>('/v1/firearms/action-types');\n }\n\n /**\n * Get available filter options for the firearms list endpoint.\n *\n * Returns distinct values for manufacturers, calibers, categories,\n * action types, countries, and statuses that can be used as filter values.\n *\n * @returns An object mapping filter fields to their available values.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.filterOptions();\n * console.log(data.manufacturers); // ['beretta', 'colt', ...]\n * console.log(data.categories); // ['pistol', 'rifle', ...]\n * ```\n */\n async filterOptions(): Promise<APIResponse<FilterOptions>> {\n return this.client.get<FilterOptions>('/v1/firearms/filter-options');\n }\n\n /**\n * Get a random firearm, optionally filtered by category or country.\n *\n * @param params - Optional category and country filters.\n * @returns A single randomly selected firearm.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.random({ category: 'pistol' });\n * console.log(data.name);\n * ```\n */\n async random(params?: RandomFirearmParams): Promise<APIResponse<Firearm>> {\n return this.client.get<Firearm>('/v1/firearms/random', params as Record<string, any>);\n }\n\n /**\n * Get top firearms ranked by a specific statistic.\n *\n * @param params - The stat to rank by and optional category/limit filters.\n * @returns An ordered array of firearms ranked by the specified stat.\n * @throws {BadRequestError} If the stat value is not a recognized ranking metric.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.top({\n * stat: 'lightest',\n * category: 'pistol',\n * limit: 5,\n * });\n * ```\n */\n async top(params: TopFirearmsParams): Promise<APIResponse<Firearm[]>> {\n return this.client.get<Firearm[]>('/v1/firearms/top', params as Record<string, any>);\n }\n\n /**\n * Compare two firearms in a head-to-head matchup.\n *\n * @param params - The slugs of the two firearms to compare.\n * @returns A detailed head-to-head comparison with per-stat breakdowns.\n * @throws {NotFoundError} If either firearm does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.headToHead({ a: 'glock-g17', b: 'sig-sauer-p320' });\n * console.log(data.winner);\n * ```\n */\n async headToHead(params: HeadToHeadParams): Promise<APIResponse<HeadToHead>> {\n return this.client.get<HeadToHead>('/v1/firearms/head-to-head', params as Record<string, any>);\n }\n\n /**\n * Filter firearms by a specific feature.\n *\n * @param params - The feature name and optional category filter with pagination.\n * @returns A paginated list of firearms that have the specified feature.\n * @throws {BadRequestError} If the feature name is empty.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.firearms.byFeature({ feature: 'threaded-barrel' });\n * ```\n */\n async byFeature(params: ByFeatureParams): Promise<PaginatedResponse<Firearm>> {\n return this.client.getPaginated<Firearm>('/v1/firearms/by-feature', params as Record<string, any>);\n }\n\n /**\n * Filter firearms by action type.\n *\n * @param params - The action type string with pagination.\n * @returns A paginated list of firearms with the specified action type.\n * @throws {BadRequestError} If the action type is empty.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.firearms.byAction({ action: 'semi-automatic' });\n * ```\n */\n async byAction(params: ByActionParams): Promise<PaginatedResponse<Firearm>> {\n return this.client.getPaginated<Firearm>('/v1/firearms/by-action', params as Record<string, any>);\n }\n\n /**\n * Filter firearms by frame/component material.\n *\n * @param params - The material name, component type, and pagination.\n * @returns A paginated list of firearms using the specified material.\n * @throws {BadRequestError} If the material or component is invalid.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.firearms.byMaterial({\n * material: 'polymer',\n * component: 'frame',\n * });\n * ```\n */\n async byMaterial(params: ByMaterialParams): Promise<PaginatedResponse<Firearm>> {\n return this.client.getPaginated<Firearm>('/v1/firearms/by-material', params as Record<string, any>);\n }\n\n /**\n * Filter firearms by designer name.\n *\n * @param params - The designer name string with pagination.\n * @returns A paginated list of firearms designed by the specified person.\n * @throws {BadRequestError} If the designer name is empty.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.firearms.byDesigner({ designer: 'John Browning' });\n * ```\n */\n async byDesigner(params: ByDesignerParams): Promise<PaginatedResponse<Firearm>> {\n return this.client.getPaginated<Firearm>('/v1/firearms/by-designer', params as Record<string, any>);\n }\n\n /**\n * Get firearms ranked by computed power rating.\n *\n * @param params - Optional category filter with pagination.\n * @returns A paginated list of firearms with their power ratings.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.firearms.powerRating({ category: 'rifle' });\n * for (const item of result.data) {\n * console.log(`${item.name}: ${item.rating}`);\n * }\n * ```\n */\n async powerRating(params?: PowerRatingParams): Promise<PaginatedResponse<PowerRating>> {\n return this.client.getPaginated<PowerRating>('/v1/firearms/power-rating', params as Record<string, any>);\n }\n\n /**\n * Get firearms arranged in a chronological timeline.\n *\n * @param params - Optional year range, category filter, and pagination.\n * @returns A paginated list of firearms ordered by introduction date.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.firearms.timeline({ from: 1900, to: 1950 });\n * ```\n */\n async timeline(params?: TimelineParams): Promise<PaginatedResponse<Firearm>> {\n return this.client.getPaginated<Firearm>('/v1/firearms/timeline', params as Record<string, any>);\n }\n\n /**\n * Filter firearms by military conflict.\n *\n * @param params - The conflict identifier string with pagination.\n * @returns A paginated list of firearms used in the specified conflict.\n * @throws {BadRequestError} If the conflict name is empty.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.firearms.byConflict({ conflict: 'world-war-ii' });\n * ```\n */\n async byConflict(params: ByConflictParams): Promise<PaginatedResponse<Firearm>> {\n return this.client.getPaginated<Firearm>('/v1/firearms/by-conflict', params as Record<string, any>);\n }\n\n // ---------------------------------------------------------------------------\n // Single-resource endpoints\n // ---------------------------------------------------------------------------\n\n /**\n * Get a single firearm by its slug or ID.\n *\n * @param id - The firearm slug (e.g. `\"glock-g17\"`) or numeric ID.\n * @returns The full firearm record with all available fields.\n * @throws {NotFoundError} If no firearm matches the given identifier.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.get('beretta-92fs');\n * console.log(data.name); // \"Beretta 92FS\"\n * console.log(data.manufacturerId); // \"beretta\"\n * ```\n */\n async get(id: string): Promise<APIResponse<Firearm>> {\n return this.client.get<Firearm>(`/v1/firearms/${encodeURIComponent(id)}`);\n }\n\n /**\n * Get all variants of a firearm.\n *\n * @param id - The parent firearm slug or ID.\n * @returns An array of variant firearms derived from the parent.\n * @throws {NotFoundError} If the parent firearm does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.getVariants('colt-1911');\n * console.log(data.map(v => v.name));\n * ```\n */\n async getVariants(id: string): Promise<APIResponse<Firearm[]>> {\n return this.client.get<Firearm[]>(`/v1/firearms/${encodeURIComponent(id)}/variants`);\n }\n\n /**\n * Get images for a firearm.\n *\n * @param id - The firearm slug or ID.\n * @returns An array of image records with URLs, dimensions, and metadata.\n * @throws {NotFoundError} If the firearm does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.getImages('sig-sauer-p226');\n * for (const img of data) {\n * console.log(img.url, img.width, img.height);\n * }\n * ```\n */\n async getImages(id: string): Promise<APIResponse<FirearmImage[]>> {\n return this.client.get<FirearmImage[]>(`/v1/firearms/${encodeURIComponent(id)}/images`);\n }\n\n /**\n * Get computed game statistics for a firearm.\n *\n * @param id - The firearm slug or ID.\n * @returns Game-balanced stats including damage, accuracy, range, etc.\n * @throws {NotFoundError} If the firearm does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.getGameStats('ak-47');\n * console.log(data.damage, data.accuracy, data.range);\n * ```\n */\n async getGameStats(id: string): Promise<APIResponse<GameStats>> {\n return this.client.get<GameStats>(`/v1/firearms/${encodeURIComponent(id)}/game-stats`);\n }\n\n /**\n * Get physical dimensions for a firearm.\n *\n * @param id - The firearm slug or ID.\n * @returns Detailed dimension data (length, height, width, barrel length, weight).\n * @throws {NotFoundError} If the firearm does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.getDimensions('glock-g19');\n * console.log(`${data.overallLengthMm}mm overall`);\n * ```\n */\n async getDimensions(id: string): Promise<APIResponse<Dimensions>> {\n return this.client.get<Dimensions>(`/v1/firearms/${encodeURIComponent(id)}/dimensions`);\n }\n\n /**\n * Get known military/law-enforcement adopters of a firearm.\n *\n * @param id - The firearm slug or ID.\n * @returns An array of users/adopters with country and organization data.\n * @throws {NotFoundError} If the firearm does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.getUsers('beretta-m9');\n * for (const user of data) {\n * console.log(user.country, user.organization);\n * }\n * ```\n */\n async getUsers(id: string): Promise<APIResponse<FirearmUser[]>> {\n return this.client.get<FirearmUser[]>(`/v1/firearms/${encodeURIComponent(id)}/users`);\n }\n\n /**\n * Get the family tree (lineage) of a firearm.\n *\n * @param id - The firearm slug or ID.\n * @returns A tree structure showing predecessors, descendants, and related models.\n * @throws {NotFoundError} If the firearm does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.getFamilyTree('m16a2');\n * console.log(data.ancestors, data.descendants);\n * ```\n */\n async getFamilyTree(id: string): Promise<APIResponse<FamilyTree>> {\n return this.client.get<FamilyTree>(`/v1/firearms/${encodeURIComponent(id)}/family-tree`);\n }\n\n /**\n * Get firearms similar to a given firearm.\n *\n * @param id - The firearm slug or ID.\n * @returns An array of similar firearms ranked by similarity score.\n * @throws {NotFoundError} If the firearm does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.getSimilar('glock-g17');\n * for (const match of data) {\n * console.log(match.name, match.similarityScore);\n * }\n * ```\n */\n async getSimilar(id: string): Promise<APIResponse<SimilarFirearm[]>> {\n return this.client.get<SimilarFirearm[]>(`/v1/firearms/${encodeURIComponent(id)}/similar`);\n }\n\n /**\n * Get the worldwide adoption map for a firearm.\n *\n * @param id - The firearm slug or ID.\n * @returns Adoption data by country with usage type and date ranges.\n * @throws {NotFoundError} If the firearm does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.getAdoptionMap('fn-fal');\n * console.log(data.countries.length, 'countries adopted this firearm');\n * ```\n */\n async getAdoptionMap(id: string): Promise<APIResponse<AdoptionMap>> {\n return this.client.get<AdoptionMap>(`/v1/firearms/${encodeURIComponent(id)}/adoption-map`);\n }\n\n /**\n * Get the full game profile for a firearm.\n *\n * @param id - The firearm slug or ID.\n * @returns Game-specific profile including archetype, tier, and stat breakdown.\n * @throws {NotFoundError} If the firearm does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.getGameProfile('mp5');\n * console.log(data.archetype, data.tier);\n * ```\n */\n async getGameProfile(id: string): Promise<APIResponse<GameProfile>> {\n return this.client.get<GameProfile>(`/v1/firearms/${encodeURIComponent(id)}/game-profile`);\n }\n\n /**\n * Get an SVG silhouette (line art) of a firearm.\n *\n * @param id - The firearm slug or ID.\n * @param params - Optional format and stroke customization parameters.\n * @returns Silhouette data in the requested format (raw SVG, data URI, or JSON).\n * @throws {NotFoundError} If the firearm does not exist or has no silhouette.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.getSilhouette('ak-47', {\n * format: 'datauri',\n * stroke_width: 2,\n * stroke_color: '#333',\n * });\n * // Use data.dataUri in an <img> tag\n * ```\n */\n async getSilhouette(id: string, params?: SilhouetteParams): Promise<APIResponse<Silhouette>> {\n return this.client.get<Silhouette>(\n `/v1/firearms/${encodeURIComponent(id)}/silhouette`,\n params as Record<string, any>,\n );\n }\n\n /**\n * Calculate ballistics for a firearm with specific ammunition.\n *\n * @param id - The firearm slug or ID.\n * @param params - Ammunition ID and optional ballistic parameters.\n * @returns Calculated ballistic data including velocity, energy, and trajectory.\n * @throws {NotFoundError} If the firearm or ammunition does not exist.\n * @throws {BadRequestError} If the ammunition is incompatible with the firearm.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.calculate('glock-g17', {\n * ammo_id: '9mm-federal-hst-124gr',\n * });\n * console.log(data.muzzleVelocity, data.muzzleEnergy);\n * ```\n */\n async calculate(id: string, params: CalculateBallisticsParams): Promise<APIResponse<FirearmCalculation>> {\n return this.client.get<FirearmCalculation>(\n `/v1/firearms/${encodeURIComponent(id)}/calculate`,\n params as Record<string, any>,\n );\n }\n\n /**\n * Load a firearm with ammunition and get combined performance data.\n *\n * @param id - The firearm slug or ID.\n * @param params - Optional ammunition ID to load.\n * @returns Combined firearm + ammunition data with computed performance metrics.\n * @throws {NotFoundError} If the firearm does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.firearms.load('sig-sauer-p226', {\n * ammo_id: '9mm-winchester-ranger-147gr',\n * });\n * ```\n */\n async load(id: string, params?: LoadFirearmParams): Promise<APIResponse<FirearmLoadProfile>> {\n return this.client.get<FirearmLoadProfile>(\n `/v1/firearms/${encodeURIComponent(id)}/load`,\n params as Record<string, any>,\n );\n }\n}\n","/**\n * Manufacturers resource for the GunSpec SDK.\n *\n * Provides access to all `/v1/manufacturers` endpoints including listing,\n * retrieving details, and exploring a manufacturer's firearms catalog,\n * timeline, and statistics.\n *\n * @module\n */\n\nimport type { HttpClient, APIResponse, PaginatedResponse } from '../core';\nimport type {\n Manufacturer,\n Firearm,\n ManufacturerTimeline,\n ManufacturerStats,\n ListManufacturersParams,\n PaginationParams,\n} from '../types';\n\n/**\n * Resource class for interacting with the GunSpec Manufacturers API.\n *\n * Wraps all `/v1/manufacturers` endpoints. Instantiated internally by the\n * {@link GunSpec} client and exposed as `client.manufacturers`.\n *\n * @example\n * ```typescript\n * import GunSpec from '@gunspec/sdk';\n *\n * const client = new GunSpec();\n *\n * // List manufacturers\n * const { data } = await client.manufacturers.list({ country: 'US' });\n *\n * // Get manufacturer details\n * const { data: mfr } = await client.manufacturers.get('beretta');\n * ```\n */\nexport class ManufacturersResource {\n constructor(private readonly client: HttpClient) {}\n\n /**\n * List manufacturers with optional filters and pagination.\n *\n * @param params - Optional query parameters for filtering by country, sorting, and pagination.\n * @returns A paginated list of manufacturers.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.manufacturers.list({\n * country: 'DE',\n * sort: 'name',\n * order: 'asc',\n * per_page: 50,\n * });\n * ```\n */\n async list(params?: ListManufacturersParams): Promise<PaginatedResponse<Manufacturer>> {\n return this.client.getPaginated<Manufacturer>('/v1/manufacturers', params as Record<string, any>);\n }\n\n /**\n * Auto-paginate through all manufacturers matching the given filters.\n *\n * Returns an async iterator that fetches pages on demand, yielding\n * individual {@link Manufacturer} objects.\n *\n * @param params - Optional query parameters for filtering and sorting.\n * @returns An async iterable iterator yielding individual manufacturers.\n *\n * @example\n * ```typescript\n * for await (const mfr of client.manufacturers.listAutoPaging()) {\n * console.log(mfr.name, mfr.country);\n * }\n * ```\n */\n async *listAutoPaging(params?: ListManufacturersParams): AsyncIterableIterator<Manufacturer> {\n let page = params?.page ?? 1;\n while (true) {\n const result = await this.list({ ...params, page });\n for (const item of result.data) {\n yield item;\n }\n if (!result.pagination.totalPages || page >= result.pagination.totalPages) break;\n page++;\n }\n }\n\n /**\n * Get a single manufacturer by its slug or ID.\n *\n * @param id - The manufacturer slug (e.g. `\"beretta\"`) or numeric ID.\n * @returns The full manufacturer record.\n * @throws {NotFoundError} If no manufacturer matches the given identifier.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.manufacturers.get('sig-sauer');\n * console.log(data.name, data.foundedYear, data.country);\n * ```\n */\n async get(id: string): Promise<APIResponse<Manufacturer>> {\n return this.client.get<Manufacturer>(`/v1/manufacturers/${encodeURIComponent(id)}`);\n }\n\n /**\n * Get all firearms produced by a manufacturer.\n *\n * @param id - The manufacturer slug or ID.\n * @param params - Optional pagination parameters.\n * @returns A paginated list of firearms from the specified manufacturer.\n * @throws {NotFoundError} If the manufacturer does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.manufacturers.getFirearms('colt', { per_page: 50 });\n * console.log(`Colt makes ${result.pagination.total} firearms`);\n * ```\n */\n async getFirearms(id: string, params?: PaginationParams): Promise<PaginatedResponse<Firearm>> {\n return this.client.getPaginated<Firearm>(\n `/v1/manufacturers/${encodeURIComponent(id)}/firearms`,\n params as Record<string, any>,\n );\n }\n\n /**\n * Get a chronological timeline for a manufacturer.\n *\n * @param id - The manufacturer slug or ID.\n * @returns Timeline data with key events, product launches, and milestones.\n * @throws {NotFoundError} If the manufacturer does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.manufacturers.getTimeline('browning');\n * for (const event of data.events) {\n * console.log(event.year, event.description);\n * }\n * ```\n */\n async getTimeline(id: string): Promise<APIResponse<ManufacturerTimeline>> {\n return this.client.get<ManufacturerTimeline>(`/v1/manufacturers/${encodeURIComponent(id)}/timeline`);\n }\n\n /**\n * Get aggregate statistics for a manufacturer.\n *\n * @param id - The manufacturer slug or ID.\n * @returns Statistical summary including firearm counts, caliber distribution, and more.\n * @throws {NotFoundError} If the manufacturer does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.manufacturers.getStats('glock');\n * console.log(data.totalFirearms, data.caliberBreakdown);\n * ```\n */\n async getStats(id: string): Promise<APIResponse<ManufacturerStats>> {\n return this.client.get<ManufacturerStats>(`/v1/manufacturers/${encodeURIComponent(id)}/stats`);\n }\n}\n","/**\n * Calibers resource for the GunSpec SDK.\n *\n * Provides access to all `/v1/calibers` endpoints including listing,\n * comparing, ballistics calculations, and exploring caliber families\n * and associated ammunition.\n *\n * @module\n */\n\nimport type { HttpClient, APIResponse, PaginatedResponse } from '../core';\nimport type {\n Caliber,\n Firearm,\n Ammunition,\n BallisticsResult,\n ListCalibersParams,\n CompareCalibersParams,\n CaliberBallisticsParams,\n PaginationParams,\n} from '../types';\n\n/**\n * Resource class for interacting with the GunSpec Calibers API.\n *\n * Wraps all `/v1/calibers` endpoints. Instantiated internally by the\n * {@link GunSpec} client and exposed as `client.calibers`.\n *\n * @example\n * ```typescript\n * import GunSpec from '@gunspec/sdk';\n *\n * const client = new GunSpec();\n *\n * // List calibers\n * const { data } = await client.calibers.list({ cartridge_type: 'centerfire' });\n *\n * // Compare calibers\n * const { data: comparison } = await client.calibers.compare({\n * ids: '9x19mm-parabellum,45-acp',\n * });\n * ```\n */\nexport class CalibersResource {\n constructor(private readonly client: HttpClient) {}\n\n /**\n * List calibers with optional filters and pagination.\n *\n * @param params - Optional query parameters for filtering by cartridge type, primer type, etc.\n * @returns A paginated list of calibers.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.calibers.list({\n * cartridge_type: 'centerfire',\n * primer_type: 'boxer',\n * per_page: 50,\n * });\n * ```\n */\n async list(params?: ListCalibersParams): Promise<PaginatedResponse<Caliber>> {\n return this.client.getPaginated<Caliber>('/v1/calibers', params as Record<string, any>);\n }\n\n /**\n * Auto-paginate through all calibers matching the given filters.\n *\n * Returns an async iterator that fetches pages on demand, yielding\n * individual {@link Caliber} objects.\n *\n * @param params - Optional query parameters for filtering and sorting.\n * @returns An async iterable iterator yielding individual calibers.\n *\n * @example\n * ```typescript\n * for await (const caliber of client.calibers.listAutoPaging()) {\n * console.log(caliber.name, caliber.bulletDiameterMm);\n * }\n * ```\n */\n async *listAutoPaging(params?: ListCalibersParams): AsyncIterableIterator<Caliber> {\n let page = params?.page ?? 1;\n while (true) {\n const result = await this.list({ ...params, page });\n for (const item of result.data) {\n yield item;\n }\n if (!result.pagination.totalPages || page >= result.pagination.totalPages) break;\n page++;\n }\n }\n\n /**\n * Compare up to 5 calibers side by side.\n *\n * @param params - Object containing comma-separated caliber IDs.\n * @returns An array of calibers with full details for comparison.\n * @throws {BadRequestError} If more than 5 IDs are provided.\n * @throws {NotFoundError} If any of the specified calibers do not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.calibers.compare({\n * ids: '9x19mm-parabellum,45-acp,40-s-w',\n * });\n * ```\n */\n async compare(params: CompareCalibersParams): Promise<APIResponse<Caliber[]>> {\n return this.client.get<Caliber[]>('/v1/calibers/compare', params as Record<string, any>);\n }\n\n /**\n * Get ballistics data for a caliber at a specified distance.\n *\n * @param params - Caliber ID and distance in meters.\n * @returns Ballistic data including velocity, energy, and drop at the specified distance.\n * @throws {NotFoundError} If the caliber does not exist.\n * @throws {BadRequestError} If the distance is out of range.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.calibers.ballistics({\n * id: '9x19mm-parabellum',\n * distance: 100,\n * });\n * console.log(data.velocity, data.energy, data.drop);\n * ```\n */\n async ballistics(params: CaliberBallisticsParams): Promise<APIResponse<BallisticsResult>> {\n return this.client.get<BallisticsResult>('/v1/calibers/ballistics', params as Record<string, any>);\n }\n\n /**\n * Get a single caliber by its slug or ID.\n *\n * @param id - The caliber slug (e.g. `\"9x19mm-parabellum\"`) or numeric ID.\n * @returns The full caliber record with all specifications.\n * @throws {NotFoundError} If no caliber matches the given identifier.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.calibers.get('45-acp');\n * console.log(data.name, data.bulletDiameterMm, data.caseLengthMm);\n * ```\n */\n async get(id: string): Promise<APIResponse<Caliber>> {\n return this.client.get<Caliber>(`/v1/calibers/${encodeURIComponent(id)}`);\n }\n\n /**\n * Get all firearms that use a specific caliber.\n *\n * @param id - The caliber slug or ID.\n * @param params - Optional pagination parameters.\n * @returns A paginated list of firearms chambered in the specified caliber.\n * @throws {NotFoundError} If the caliber does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.calibers.getFirearms('9x19mm-parabellum', { per_page: 50 });\n * console.log(`${result.pagination.total} firearms use 9mm`);\n * ```\n */\n async getFirearms(id: string, params?: PaginationParams): Promise<PaginatedResponse<Firearm>> {\n return this.client.getPaginated<Firearm>(\n `/v1/calibers/${encodeURIComponent(id)}/firearms`,\n params as Record<string, any>,\n );\n }\n\n /**\n * Get the parent caliber chain (ancestry) for a caliber.\n *\n * @param id - The caliber slug or ID.\n * @returns An ordered array of calibers from the given caliber up to the root ancestor.\n * @throws {NotFoundError} If the caliber does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.calibers.getParentChain('300-blackout');\n * // [300 Blackout, 5.56x45mm NATO, .223 Remington, ...]\n * ```\n */\n async getParentChain(id: string): Promise<APIResponse<Caliber[]>> {\n return this.client.get<Caliber[]>(`/v1/calibers/${encodeURIComponent(id)}/parent-chain`);\n }\n\n /**\n * Get the full family tree of related calibers.\n *\n * @param id - The caliber slug or ID.\n * @returns An array of calibers in the same family (parent, siblings, children).\n * @throws {NotFoundError} If the caliber does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.calibers.getFamily('9x19mm-parabellum');\n * console.log(data.map(c => c.name));\n * ```\n */\n async getFamily(id: string): Promise<APIResponse<Caliber[]>> {\n return this.client.get<Caliber[]>(`/v1/calibers/${encodeURIComponent(id)}/family`);\n }\n\n /**\n * Get ammunition loads available for a caliber.\n *\n * @param id - The caliber slug or ID.\n * @param params - Optional pagination parameters.\n * @returns A paginated list of ammunition loads for the specified caliber.\n * @throws {NotFoundError} If the caliber does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.calibers.getAmmunition('9x19mm-parabellum', { per_page: 25 });\n * for (const ammo of result.data) {\n * console.log(ammo.name, ammo.bulletWeightGrains);\n * }\n * ```\n */\n async getAmmunition(id: string, params?: PaginationParams): Promise<PaginatedResponse<Ammunition>> {\n return this.client.getPaginated<Ammunition>(\n `/v1/calibers/${encodeURIComponent(id)}/ammunition`,\n params as Record<string, any>,\n );\n }\n}\n","/**\n * Categories resource for the GunSpec SDK.\n *\n * Provides access to `/v1/categories` endpoints for listing firearm\n * categories and retrieving firearms within a specific category.\n *\n * @module\n */\n\nimport type { HttpClient, APIResponse, PaginatedResponse } from '../core';\nimport type {\n Category,\n Firearm,\n PaginationParams,\n} from '../types';\n\n/**\n * Resource class for interacting with the GunSpec Categories API.\n *\n * Wraps all `/v1/categories` endpoints. Instantiated internally by the\n * {@link GunSpec} client and exposed as `client.categories`.\n *\n * @example\n * ```typescript\n * import GunSpec from '@gunspec/sdk';\n *\n * const client = new GunSpec();\n *\n * // List all categories\n * const { data } = await client.categories.list();\n * console.log(data.map(c => c.name));\n *\n * // Get firearms in a category\n * const pistols = await client.categories.getFirearms('pistol');\n * ```\n */\nexport class CategoriesResource {\n constructor(private readonly client: HttpClient) {}\n\n /**\n * List all firearm categories.\n *\n * @returns An array of all category records.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.categories.list();\n * // [{ slug: 'pistol', name: 'Pistol' }, { slug: 'rifle', name: 'Rifle' }, ...]\n * ```\n */\n async list(): Promise<APIResponse<Category[]>> {\n return this.client.get<Category[]>('/v1/categories');\n }\n\n /**\n * Get all firearms within a specific category.\n *\n * @param slug - The category slug (e.g. `\"pistol\"`, `\"rifle\"`, `\"shotgun\"`).\n * @param params - Optional pagination parameters.\n * @returns A paginated list of firearms in the specified category.\n * @throws {NotFoundError} If the category slug does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.categories.getFirearms('shotgun', {\n * per_page: 25,\n * sort: 'name',\n * order: 'asc',\n * });\n * console.log(`${result.pagination.total} shotguns in database`);\n * ```\n */\n async getFirearms(slug: string, params?: PaginationParams): Promise<PaginatedResponse<Firearm>> {\n return this.client.getPaginated<Firearm>(\n `/v1/categories/${encodeURIComponent(slug)}/firearms`,\n params as Record<string, any>,\n );\n }\n}\n","/**\n * Statistics resource for the GunSpec SDK.\n *\n * Provides access to all `/v1/stats` endpoints for retrieving aggregate\n * statistics about the firearms database including production status,\n * field coverage, caliber popularity, and more.\n *\n * @module\n */\n\nimport type { HttpClient, APIResponse } from '../core';\nimport type {\n StatsSummary,\n ProductionStatusItem,\n FieldCoverage,\n PopularCaliber,\n ProlificManufacturer,\n CategoryStats,\n EraStats,\n MaterialStats,\n AdoptionByCountryItem,\n AdoptionByTypeItem,\n ActionTypeStats,\n FeatureFrequency,\n CaliberPopularityByEra,\n PopularCalibersParams,\n ProlificManufacturersParams,\n ByEraParams,\n AdoptionByCountryParams,\n AdoptionByTypeParams,\n ActionTypesParams,\n FeatureFrequencyParams,\n CaliberPopularityByEraParams,\n} from '../types';\n\n/**\n * Resource class for interacting with the GunSpec Statistics API.\n *\n * Wraps all `/v1/stats` endpoints. Instantiated internally by the\n * {@link GunSpec} client and exposed as `client.stats`.\n *\n * @example\n * ```typescript\n * import GunSpec from '@gunspec/sdk';\n *\n * const client = new GunSpec();\n *\n * const { data } = await client.stats.summary();\n * console.log(data.totalFirearms, data.totalManufacturers);\n * ```\n */\nexport class StatsResource {\n constructor(private readonly client: HttpClient) {}\n\n /**\n * Get a high-level summary of the database.\n *\n * @returns Summary counts for firearms, manufacturers, calibers, and categories.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.stats.summary();\n * console.log(`Database has ${data.totalFirearms} firearms`);\n * ```\n */\n async summary(): Promise<APIResponse<StatsSummary>> {\n return this.client.get<StatsSummary>('/v1/stats/summary');\n }\n\n /**\n * Get firearm counts grouped by production status.\n *\n * @returns Counts for in-production, discontinued, and prototype firearms.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.stats.productionStatus();\n * // { in_production: 342, discontinued: 891, prototype: 12 }\n * ```\n */\n async productionStatus(): Promise<APIResponse<ProductionStatusItem[]>> {\n return this.client.get<ProductionStatusItem[]>('/v1/stats/production-status');\n }\n\n /**\n * Get field coverage statistics across the database.\n *\n * Shows the percentage of firearms that have data for each field,\n * useful for assessing data completeness.\n *\n * @returns Per-field coverage percentages.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.stats.fieldCoverage();\n * console.log(`Weight field: ${data.weight}% coverage`);\n * ```\n */\n async fieldCoverage(): Promise<APIResponse<FieldCoverage>> {\n return this.client.get<FieldCoverage>('/v1/stats/field-coverage');\n }\n\n /**\n * Get the most popular calibers by firearm count.\n *\n * @param params - Optional limit parameter.\n * @returns An ordered list of calibers ranked by the number of firearms using them.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.stats.popularCalibers({ limit: 10 });\n * for (const entry of data) {\n * console.log(`${entry.name}: ${entry.count} firearms`);\n * }\n * ```\n */\n async popularCalibers(params?: PopularCalibersParams): Promise<APIResponse<PopularCaliber[]>> {\n return this.client.get<PopularCaliber[]>('/v1/stats/calibers/popular', params as Record<string, any>);\n }\n\n /**\n * Get the most prolific manufacturers by firearm count.\n *\n * @param params - Optional limit and category filter.\n * @returns An ordered list of manufacturers ranked by the number of firearms produced.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.stats.prolificManufacturers({\n * limit: 10,\n * category: 'pistol',\n * });\n * ```\n */\n async prolificManufacturers(params?: ProlificManufacturersParams): Promise<APIResponse<ProlificManufacturer[]>> {\n return this.client.get<ProlificManufacturer[]>('/v1/stats/manufacturers/prolific', params as Record<string, any>);\n }\n\n /**\n * Get firearm counts grouped by category.\n *\n * @returns Counts per category (pistol, rifle, shotgun, etc.).\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.stats.byCategory();\n * // [{ category: 'pistol', count: 512 }, { category: 'rifle', count: 234 }, ...]\n * ```\n */\n async byCategory(): Promise<APIResponse<CategoryStats[]>> {\n return this.client.get<CategoryStats[]>('/v1/stats/by-category');\n }\n\n /**\n * Get firearm statistics for a specific decade/era.\n *\n * @param params - The decade string (e.g. `\"1990s\"`).\n * @returns Statistics for firearms introduced during the specified decade.\n * @throws {BadRequestError} If the decade format is invalid (must be like `\"1990s\"`).\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.stats.byEra({ decade: '1940s' });\n * ```\n */\n async byEra(params: ByEraParams): Promise<APIResponse<EraStats>> {\n return this.client.get<EraStats>('/v1/stats/by-era', params as Record<string, any>);\n }\n\n /**\n * Get statistics about materials used across all firearms.\n *\n * @returns Material usage counts and percentages by component type.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.stats.materials();\n * ```\n */\n async materials(): Promise<APIResponse<MaterialStats>> {\n return this.client.get<MaterialStats>('/v1/stats/materials');\n }\n\n /**\n * Get firearm adoption statistics for a specific country.\n *\n * @param params - The country code (e.g. `\"US\"`, `\"GB\"`).\n * @returns Adoption data for the specified country.\n * @throws {BadRequestError} If the country code is invalid.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.stats.adoptionByCountry({ code: 'US' });\n * ```\n */\n async adoptionByCountry(params: AdoptionByCountryParams): Promise<APIResponse<AdoptionByCountryItem[]>> {\n return this.client.get<AdoptionByCountryItem[]>('/v1/stats/adoption/by-country', params as Record<string, any>);\n }\n\n /**\n * Get firearm adoption statistics by usage type.\n *\n * @param params - The usage type (e.g. `\"military\"`, `\"law_enforcement\"`, `\"civilian\"`).\n * @returns Adoption data for the specified usage type.\n * @throws {BadRequestError} If the type is not a recognized usage category.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.stats.adoptionByType({ type: 'military' });\n * ```\n */\n async adoptionByType(params: AdoptionByTypeParams): Promise<APIResponse<AdoptionByTypeItem[]>> {\n return this.client.get<AdoptionByTypeItem[]>('/v1/stats/adoption/by-type', params as Record<string, any>);\n }\n\n /**\n * Get firearm counts grouped by action type.\n *\n * @param params - Optional category filter.\n * @returns Counts per action type, optionally filtered by category.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.stats.actionTypes({ category: 'rifle' });\n * ```\n */\n async actionTypes(params?: ActionTypesParams): Promise<APIResponse<ActionTypeStats[]>> {\n return this.client.get<ActionTypeStats[]>('/v1/stats/action-types', params as Record<string, any>);\n }\n\n /**\n * Get feature frequency statistics across the database.\n *\n * @param params - Optional category filter and result limit.\n * @returns An ordered list of features ranked by frequency of occurrence.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.stats.featureFrequency({\n * category: 'pistol',\n * limit: 20,\n * });\n * ```\n */\n async featureFrequency(params?: FeatureFrequencyParams): Promise<APIResponse<FeatureFrequency[]>> {\n return this.client.get<FeatureFrequency[]>('/v1/stats/feature-frequency', params as Record<string, any>);\n }\n\n /**\n * Get caliber popularity trends across historical eras.\n *\n * @param params - Optional from/to decade range (e.g. `\"1940s\"` to `\"2020s\"`).\n * @returns Caliber popularity data broken down by decade.\n * @throws {BadRequestError} If the decade format is invalid.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.stats.caliberPopularityByEra({\n * from_decade: '1940s',\n * to_decade: '2020s',\n * });\n * ```\n */\n async caliberPopularityByEra(params?: CaliberPopularityByEraParams): Promise<APIResponse<CaliberPopularityByEra[]>> {\n return this.client.get<CaliberPopularityByEra[]>('/v1/stats/caliber-popularity-by-era', params as Record<string, any>);\n }\n}\n","/**\n * Game resource for the GunSpec SDK.\n *\n * Provides access to all `/v1/game` endpoints for game-balanced\n * firearm data including balance reports, tier lists, matchups,\n * role rosters, and stat distributions.\n *\n * @module\n */\n\nimport type { HttpClient, APIResponse } from '../core';\nimport type {\n BalanceEntry,\n TierList,\n MatchupResult,\n RoleRosterItem,\n StatDistribution,\n BalanceReportParams,\n TierListParams,\n MatchupsParams,\n RoleRosterParams,\n StatDistributionParams,\n} from '../types';\n\n/**\n * Resource class for interacting with the GunSpec Game API.\n *\n * Wraps all `/v1/game` endpoints. These endpoints provide game-balanced\n * firearm data suitable for game development, including normalized stats,\n * tier rankings, and balance analysis.\n *\n * Instantiated internally by the {@link GunSpec} client and exposed as\n * `client.game`.\n *\n * @example\n * ```typescript\n * import GunSpec from '@gunspec/sdk';\n *\n * const client = new GunSpec();\n *\n * const { data } = await client.game.tierList({ stat: 'damage' });\n * console.log(data.tiers);\n * ```\n */\nexport class GameResource {\n constructor(private readonly client: HttpClient) {}\n\n /**\n * Get a balance report showing outlier firearms.\n *\n * Identifies firearms whose game stats deviate significantly from\n * the average, useful for game balancing.\n *\n * @param params - Optional threshold for outlier detection (0-100).\n * @returns A balance report with overpowered and underpowered firearms.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.game.balanceReport({ threshold: 15 });\n * console.log(data.length, 'firearms flagged as outliers');\n * ```\n */\n async balanceReport(params?: BalanceReportParams): Promise<APIResponse<BalanceEntry[]>> {\n return this.client.get<BalanceEntry[]>('/v1/game/balance-report', params as Record<string, any>);\n }\n\n /**\n * Get a tier list of firearms ranked by a specific stat.\n *\n * @param params - Optional category filter and stat to rank by.\n * @returns A tier list with firearms grouped into S/A/B/C/D/F tiers.\n * @throws {BadRequestError} If the stat is not a valid game stat.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.game.tierList({\n * stat: 'accuracy',\n * category: 'rifle',\n * });\n * console.log('S-tier:', data.tiers.S.map(f => f.name));\n * ```\n */\n async tierList(params?: TierListParams): Promise<APIResponse<TierList>> {\n return this.client.get<TierList>('/v1/game/tier-list', params as Record<string, any>);\n }\n\n /**\n * Get a game-balanced matchup between two firearms.\n *\n * @param params - The slugs of the two firearms to compare.\n * @returns A detailed matchup result with per-stat comparisons and a winner.\n * @throws {NotFoundError} If either firearm does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.game.matchups({ a: 'ak-47', b: 'm4-carbine' });\n * console.log(data.winner, data.statComparisons);\n * ```\n */\n async matchups(params: MatchupsParams): Promise<APIResponse<MatchupResult>> {\n return this.client.get<MatchupResult>('/v1/game/matchups', params as Record<string, any>);\n }\n\n /**\n * Get a roster of firearms best suited for a specific game role.\n *\n * @param params - The role to query and optional count limit.\n * @returns A roster of firearms ranked by suitability for the given role.\n * @throws {BadRequestError} If the role is not a recognized game role.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.game.roleRoster({\n * role: 'sniper',\n * count: 10,\n * });\n * for (const firearm of data) {\n * console.log(firearm.name, firearm.roleScore);\n * }\n * ```\n */\n async roleRoster(params: RoleRosterParams): Promise<APIResponse<RoleRosterItem[]>> {\n return this.client.get<RoleRosterItem[]>('/v1/game/role-roster', params as Record<string, any>);\n }\n\n /**\n * Get the statistical distribution for a specific game stat.\n *\n * Shows how firearms are distributed across value ranges for a given\n * stat, useful for understanding the spread and identifying balance issues.\n *\n * @param params - The stat to analyze (e.g. `\"damage\"`, `\"accuracy\"`).\n * @returns Distribution data including histogram buckets and summary statistics.\n * @throws {BadRequestError} If the stat is not a valid game stat.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.game.statDistribution({ stat: 'damage' });\n * console.log(data.mean, data.median, data.buckets);\n * ```\n */\n async statDistribution(params: StatDistributionParams): Promise<APIResponse<StatDistribution>> {\n return this.client.get<StatDistribution>('/v1/game/stat-distribution', params as Record<string, any>);\n }\n}\n","/**\n * Game Stats Snapshots resource for the GunSpec SDK.\n *\n * Provides access to `/v1/game-stats` endpoints for retrieving versioned\n * snapshots of game-balanced firearm statistics. Each snapshot captures\n * the game stats at a specific point in time, enabling version tracking\n * and historical comparisons.\n *\n * @module\n */\n\nimport type { HttpClient, APIResponse, PaginatedResponse } from '../core';\nimport type {\n GameStatsVersion,\n GameStatsSnapshotEntry,\n ListSnapshotFirearmsParams,\n} from '../types';\n\n/**\n * Resource class for interacting with the GunSpec Game Stats Snapshots API.\n *\n * Wraps all `/v1/game-stats` endpoints. Instantiated internally by the\n * {@link GunSpec} client and exposed as `client.gameStats`.\n *\n * @example\n * ```typescript\n * import GunSpec from '@gunspec/sdk';\n *\n * const client = new GunSpec();\n *\n * // List all snapshot versions\n * const { data: versions } = await client.gameStats.listVersions();\n *\n * // Get firearms from a specific version\n * const result = await client.gameStats.listFirearms('1.0.0');\n * ```\n */\nexport class GameStatsResource {\n constructor(private readonly client: HttpClient) {}\n\n /**\n * List all available game stats snapshot versions.\n *\n * @returns An array of version records with metadata about each snapshot.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.gameStats.listVersions();\n * for (const version of data) {\n * console.log(version.version, version.createdAt, version.description);\n * }\n * ```\n */\n async listVersions(): Promise<APIResponse<GameStatsVersion[]>> {\n return this.client.get<GameStatsVersion[]>('/v1/game-stats/versions');\n }\n\n /**\n * List all firearms in a specific game stats snapshot version.\n *\n * @param version - The snapshot version string (e.g. `\"1.0.0\"`).\n * @param params - Optional pagination parameters.\n * @returns A paginated list of firearms with their game stats for the given version.\n * @throws {NotFoundError} If the specified version does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.gameStats.listFirearms('1.0.0', { per_page: 50 });\n * for (const firearm of result.data) {\n * console.log(firearm.name, firearm.damage);\n * }\n * ```\n */\n async listFirearms(version: string, params?: ListSnapshotFirearmsParams): Promise<PaginatedResponse<GameStatsSnapshotEntry>> {\n return this.client.getPaginated<GameStatsSnapshotEntry>(\n `/v1/game-stats/versions/${encodeURIComponent(version)}/firearms`,\n params as Record<string, any>,\n );\n }\n\n /**\n * Get a single firearm's game stats from a specific snapshot version.\n *\n * @param version - The snapshot version string (e.g. `\"1.0.0\"`).\n * @param id - The firearm slug or ID.\n * @returns The firearm's game stats as captured in the specified version.\n * @throws {NotFoundError} If the version or firearm does not exist in the snapshot.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.gameStats.getFirearm('1.0.0', 'glock-g17');\n * console.log(data.damage, data.accuracy, data.range);\n * ```\n */\n async getFirearm(version: string, id: string): Promise<APIResponse<GameStatsSnapshotEntry>> {\n return this.client.get<GameStatsSnapshotEntry>(\n `/v1/game-stats/versions/${encodeURIComponent(version)}/firearms/${encodeURIComponent(id)}`,\n );\n }\n}\n","/**\n * Ammunition resource for the GunSpec SDK.\n *\n * Provides access to all `/v1/ammunition` endpoints including listing,\n * retrieving details, bullet SVG generation, and ballistic calculations.\n *\n * @module\n */\n\nimport type { HttpClient, APIResponse, PaginatedResponse } from '../core';\nimport type {\n Ammunition,\n BallisticProfile,\n ListAmmunitionParams,\n AmmunitionBallisticsParams,\n} from '../types';\n\n/**\n * Resource class for interacting with the GunSpec Ammunition API.\n *\n * Wraps all `/v1/ammunition` endpoints. Instantiated internally by the\n * {@link GunSpec} client and exposed as `client.ammunition`.\n *\n * @example\n * ```typescript\n * import GunSpec from '@gunspec/sdk';\n *\n * const client = new GunSpec();\n *\n * // List ammunition\n * const { data } = await client.ammunition.list({ caliber_id: '9x19mm-parabellum' });\n *\n * // Get ballistics for a specific load\n * const { data: ballistics } = await client.ammunition.ballistics(\n * '9mm-federal-hst-124gr',\n * { distances: '0,25,50,100' },\n * );\n * ```\n */\nexport class AmmunitionResource {\n constructor(private readonly client: HttpClient) {}\n\n /**\n * List ammunition with optional filters and pagination.\n *\n * @param params - Optional query parameters for filtering by caliber, bullet type, etc.\n * @returns A paginated list of ammunition loads.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.ammunition.list({\n * caliber_id: '9x19mm-parabellum',\n * bullet_type: 'hollow-point',\n * per_page: 25,\n * });\n * ```\n */\n async list(params?: ListAmmunitionParams): Promise<PaginatedResponse<Ammunition>> {\n return this.client.getPaginated<Ammunition>('/v1/ammunition', params as Record<string, any>);\n }\n\n /**\n * Auto-paginate through all ammunition matching the given filters.\n *\n * Returns an async iterator that fetches pages on demand, yielding\n * individual {@link Ammunition} objects.\n *\n * @param params - Optional query parameters for filtering and sorting.\n * @returns An async iterable iterator yielding individual ammunition loads.\n *\n * @example\n * ```typescript\n * for await (const ammo of client.ammunition.listAutoPaging({ caliber_id: '45-acp' })) {\n * console.log(ammo.name, ammo.bulletWeightGrains);\n * }\n * ```\n */\n async *listAutoPaging(params?: ListAmmunitionParams): AsyncIterableIterator<Ammunition> {\n let page = params?.page ?? 1;\n while (true) {\n const result = await this.list({ ...params, page });\n for (const item of result.data) {\n yield item;\n }\n if (!result.pagination.totalPages || page >= result.pagination.totalPages) break;\n page++;\n }\n }\n\n /**\n * Get a single ammunition load by its slug or ID.\n *\n * @param id - The ammunition slug (e.g. `\"9mm-federal-hst-124gr\"`) or numeric ID.\n * @returns The full ammunition record with specifications.\n * @throws {NotFoundError} If no ammunition matches the given identifier.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.ammunition.get('9mm-federal-hst-124gr');\n * console.log(data.name, data.muzzleVelocityFps, data.bulletWeightGrains);\n * ```\n */\n async get(id: string): Promise<APIResponse<Ammunition>> {\n return this.client.get<Ammunition>(`/v1/ammunition/${encodeURIComponent(id)}`);\n }\n\n /**\n * Get a bullet profile SVG image for an ammunition load.\n *\n * Returns a raw SVG string representing the bullet projectile shape.\n * This endpoint returns raw SVG content, not the standard JSON envelope.\n *\n * @param id - The ammunition slug or ID.\n * @returns The raw SVG string of the bullet profile.\n * @throws {NotFoundError} If the ammunition does not exist.\n *\n * @example\n * ```typescript\n * const svg = await client.ammunition.getBulletSvg('9mm-federal-hst-124gr');\n * // Insert into DOM: element.innerHTML = svg;\n * ```\n */\n async getBulletSvg(id: string): Promise<string> {\n const response = await this.client.get<string>(\n `/v1/ammunition/${encodeURIComponent(id)}/bullet.svg`,\n );\n return response.data;\n }\n\n /**\n * Get ballistic trajectory data for an ammunition load.\n *\n * @param id - The ammunition slug or ID.\n * @param params - Optional barrel length and distance parameters.\n * @returns Ballistic trajectory data at specified distances.\n * @throws {NotFoundError} If the ammunition does not exist.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.ammunition.ballistics('9mm-federal-hst-124gr', {\n * barrel_length_mm: 102,\n * distances: '0,25,50,100,200',\n * });\n * for (const point of data.trajectory) {\n * console.log(`${point.distanceM}m: ${point.velocityFps} fps, ${point.energyFtLbs} ft-lbs`);\n * }\n * ```\n */\n async ballistics(id: string, params?: AmmunitionBallisticsParams): Promise<APIResponse<BallisticProfile>> {\n return this.client.get<BallisticProfile>(\n `/v1/ammunition/${encodeURIComponent(id)}/ballistics`,\n params as Record<string, any>,\n );\n }\n}\n","/**\n * Countries resource for the GunSpec SDK.\n *\n * Provides access to `/v1/countries` endpoints for listing countries\n * and retrieving a country's full firearm arsenal.\n *\n * @module\n */\n\nimport type { HttpClient, APIResponse } from '../core';\nimport type {\n Country,\n CountryArsenal,\n} from '../types';\n\n/**\n * Resource class for interacting with the GunSpec Countries API.\n *\n * Wraps all `/v1/countries` endpoints. Instantiated internally by the\n * {@link GunSpec} client and exposed as `client.countries`.\n *\n * @example\n * ```typescript\n * import GunSpec from '@gunspec/sdk';\n *\n * const client = new GunSpec();\n *\n * // List all countries\n * const { data } = await client.countries.list();\n *\n * // Get a country's full arsenal\n * const { data: arsenal } = await client.countries.getArsenal('US');\n * ```\n */\nexport class CountriesResource {\n constructor(private readonly client: HttpClient) {}\n\n /**\n * List all countries that have adopted at least one firearm.\n *\n * @returns An array of country records with codes and names.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.countries.list();\n * for (const country of data) {\n * console.log(country.code, country.name);\n * }\n * ```\n */\n async list(): Promise<APIResponse<Country[]>> {\n return this.client.get<Country[]>('/v1/countries');\n }\n\n /**\n * Get the full firearm arsenal for a specific country.\n *\n * Returns all firearms adopted by the country, grouped by usage type\n * (military, law enforcement, etc.).\n *\n * @param code - The country code (e.g. `\"US\"`, `\"GB\"`, `\"DE\"`).\n * @returns The country's arsenal data with firearms grouped by adoption type.\n * @throws {NotFoundError} If the country code is not found in the database.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.countries.getArsenal('US');\n * console.log(`${data.country.name} has ${data.totalFirearms} firearms`);\n * for (const group of data.groups) {\n * console.log(`${group.type}: ${group.firearms.length} firearms`);\n * }\n * ```\n */\n async getArsenal(code: string): Promise<APIResponse<CountryArsenal>> {\n return this.client.get<CountryArsenal>(`/v1/countries/${encodeURIComponent(code)}/arsenal`);\n }\n}\n","/**\n * Conflicts resource for the GunSpec SDK.\n *\n * Provides access to the `/v1/conflicts` endpoint for listing military\n * conflicts and their associated firearms.\n *\n * @module\n */\n\nimport type { HttpClient, APIResponse } from '../core';\nimport type { Conflict } from '../types';\n\n/**\n * Resource class for interacting with the GunSpec Conflicts API.\n *\n * Wraps the `/v1/conflicts` endpoint. Instantiated internally by the\n * {@link GunSpec} client and exposed as `client.conflicts`.\n *\n * @example\n * ```typescript\n * import GunSpec from '@gunspec/sdk';\n *\n * const client = new GunSpec();\n *\n * const { data } = await client.conflicts.list();\n * for (const conflict of data) {\n * console.log(conflict.name, conflict.startYear, conflict.endYear);\n * }\n * ```\n */\nexport class ConflictsResource {\n constructor(private readonly client: HttpClient) {}\n\n /**\n * List all military conflicts in the database.\n *\n * Returns conflicts with metadata including name, date range, and\n * participating nations. Use the conflict identifiers with\n * `client.firearms.byConflict()` to find firearms used in a specific conflict.\n *\n * @returns An array of all conflict records.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.conflicts.list();\n * for (const conflict of data) {\n * console.log(`${conflict.name} (${conflict.startYear}-${conflict.endYear})`);\n * }\n *\n * // Then find firearms used in a conflict:\n * const wwii = await client.firearms.byConflict({ conflict: 'world-war-ii' });\n * ```\n */\n async list(): Promise<APIResponse<Conflict[]>> {\n return this.client.get<Conflict[]>('/v1/conflicts');\n }\n}\n","/**\n * Data Quality resource for the GunSpec SDK.\n *\n * Provides access to `/v1/data` endpoints for assessing database\n * coverage and per-record confidence scores.\n *\n * @module\n */\n\nimport type { HttpClient, APIResponse, PaginatedResponse } from '../core';\nimport type {\n DataCoverage,\n ConfidenceEntry,\n ConfidenceParams,\n} from '../types';\n\n/**\n * Resource class for interacting with the GunSpec Data Quality API.\n *\n * Wraps all `/v1/data` endpoints. Instantiated internally by the\n * {@link GunSpec} client and exposed as `client.dataQuality`.\n *\n * @example\n * ```typescript\n * import GunSpec from '@gunspec/sdk';\n *\n * const client = new GunSpec();\n *\n * // Check overall data coverage\n * const { data } = await client.dataQuality.coverage();\n * console.log(`${data.overallCoverage}% overall field coverage`);\n *\n * // Find low-confidence records\n * const lowConf = await client.dataQuality.confidence({ below: 0.3 });\n * ```\n */\nexport class DataQualityResource {\n constructor(private readonly client: HttpClient) {}\n\n /**\n * Get overall data coverage statistics for the database.\n *\n * Returns per-field and aggregate coverage metrics showing what\n * percentage of records have data for each field.\n *\n * @returns Data coverage statistics including per-field percentages.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const { data } = await client.dataQuality.coverage();\n * console.log(`Overall: ${data.overallCoverage}%`);\n * for (const field of data.fields) {\n * console.log(`${field.name}: ${field.coverage}%`);\n * }\n * ```\n */\n async coverage(): Promise<APIResponse<DataCoverage>> {\n return this.client.get<DataCoverage>('/v1/data/coverage');\n }\n\n /**\n * Get firearms with confidence scores below a specified threshold.\n *\n * Useful for identifying records that may need additional data\n * verification or enrichment.\n *\n * @param params - Optional threshold and pagination parameters.\n * @returns A paginated list of firearms with their confidence scores.\n * @throws {BadRequestError} If the threshold is outside the 0-1 range.\n * @throws {AuthenticationError} If the API key is missing or invalid.\n *\n * @example\n * ```typescript\n * const result = await client.dataQuality.confidence({\n * below: 0.5,\n * per_page: 25,\n * });\n * for (const item of result.data) {\n * console.log(`${item.name}: confidence ${item.confidenceScore}`);\n * }\n * ```\n */\n async confidence(params?: ConfidenceParams): Promise<PaginatedResponse<ConfidenceEntry>> {\n return this.client.getPaginated<ConfidenceEntry>('/v1/data/confidence', params as Record<string, any>);\n }\n}\n","import type { HttpClient, APIResponse, PaginatedResponse } from '../core';\nimport type {\n Favorite,\n FavoriteToggle,\n ListFavoritesParams,\n} from '../types';\n\nexport class FavoritesResource {\n constructor(private readonly client: HttpClient) {}\n\n async list(params?: ListFavoritesParams): Promise<PaginatedResponse<Favorite>> {\n return this.client.getPaginated<Favorite>('/v1/me/favorites', params as Record<string, any>);\n }\n\n async add(firearmId: string): Promise<APIResponse<FavoriteToggle>> {\n return this.client.post<FavoriteToggle>(`/v1/me/favorites/${encodeURIComponent(firearmId)}`);\n }\n\n async remove(firearmId: string): Promise<APIResponse<FavoriteToggle>> {\n return this.client.delete<FavoriteToggle>(`/v1/me/favorites/${encodeURIComponent(firearmId)}`);\n }\n}\n","import type { HttpClient, APIResponse, PaginatedResponse } from '../core';\nimport type {\n DataReport,\n CreateReportParams,\n ListReportsParams,\n} from '../types';\n\nexport class ReportsResource {\n constructor(private readonly client: HttpClient) {}\n\n async create(params: CreateReportParams): Promise<APIResponse<DataReport>> {\n return this.client.post<DataReport>('/v1/me/reports', params);\n }\n\n async list(params?: ListReportsParams): Promise<PaginatedResponse<DataReport>> {\n return this.client.getPaginated<DataReport>('/v1/me/reports', params as Record<string, any>);\n }\n}\n","import type { HttpClient, APIResponse, PaginatedResponse } from '../core';\nimport type {\n SupportTicket,\n SupportTicketDetail,\n SupportTicketReply,\n CreateTicketParams,\n ListTicketsParams,\n CreateReplyParams,\n} from '../types';\n\nexport class SupportResource {\n constructor(private readonly client: HttpClient) {}\n\n async create(params: CreateTicketParams): Promise<APIResponse<SupportTicket>> {\n return this.client.post<SupportTicket>('/v1/me/support', params);\n }\n\n async list(params?: ListTicketsParams): Promise<PaginatedResponse<SupportTicket>> {\n return this.client.getPaginated<SupportTicket>('/v1/me/support', params as Record<string, any>);\n }\n\n async get(ticketId: string): Promise<APIResponse<SupportTicketDetail>> {\n return this.client.get<SupportTicketDetail>(`/v1/me/support/${encodeURIComponent(ticketId)}`);\n }\n\n async reply(ticketId: string, params: CreateReplyParams): Promise<APIResponse<SupportTicketReply>> {\n return this.client.post<SupportTicketReply>(`/v1/me/support/${encodeURIComponent(ticketId)}/replies`, params);\n }\n}\n","import type { HttpClient, APIResponse, PaginatedResponse } from '../core';\nimport type {\n WebhookEndpoint,\n WebhookTestResult,\n CreateWebhookEndpointParams,\n UpdateWebhookEndpointParams,\n ListWebhookEndpointsParams,\n} from '../types';\n\nexport class WebhooksResource {\n constructor(private readonly client: HttpClient) {}\n\n async list(params?: ListWebhookEndpointsParams): Promise<PaginatedResponse<WebhookEndpoint>> {\n return this.client.getPaginated<WebhookEndpoint>('/v1/me/webhooks', params as Record<string, any>);\n }\n\n async create(params: CreateWebhookEndpointParams): Promise<APIResponse<WebhookEndpoint>> {\n return this.client.post<WebhookEndpoint>('/v1/me/webhooks', params);\n }\n\n async get(id: string): Promise<APIResponse<WebhookEndpoint>> {\n return this.client.get<WebhookEndpoint>(`/v1/me/webhooks/${encodeURIComponent(id)}`);\n }\n\n async update(id: string, params: UpdateWebhookEndpointParams): Promise<APIResponse<WebhookEndpoint>> {\n return this.client.put<WebhookEndpoint>(`/v1/me/webhooks/${encodeURIComponent(id)}`, params);\n }\n\n async delete(id: string): Promise<APIResponse<{ deleted: boolean }>> {\n return this.client.delete<{ deleted: boolean }>(`/v1/me/webhooks/${encodeURIComponent(id)}`);\n }\n\n async test(id: string): Promise<APIResponse<WebhookTestResult>> {\n return this.client.post<WebhookTestResult>(`/v1/me/webhooks/${encodeURIComponent(id)}/test`);\n }\n}\n","import type { HttpClient, APIResponse } from '../core';\nimport type {\n UsageStats,\n UsageParams,\n} from '../types';\n\nexport class UsageResource {\n constructor(private readonly client: HttpClient) {}\n\n async get(params?: UsageParams): Promise<APIResponse<UsageStats>> {\n return this.client.get<UsageStats>('/v1/me/usage', params as Record<string, any>);\n }\n}\n","/** Current SDK version */\nexport const VERSION = '0.1.0'\n","import { HttpClient } from './core/http-client'\nimport type { RetryConfig } from './core/retry'\nimport { FirearmsResource } from './resources/firearms'\nimport { ManufacturersResource } from './resources/manufacturers'\nimport { CalibersResource } from './resources/calibers'\nimport { CategoriesResource } from './resources/categories'\nimport { StatsResource } from './resources/stats'\nimport { GameResource } from './resources/game'\nimport { GameStatsResource } from './resources/game-stats'\nimport { AmmunitionResource } from './resources/ammunition'\nimport { CountriesResource } from './resources/countries'\nimport { ConflictsResource } from './resources/conflicts'\nimport { DataQualityResource } from './resources/data-quality'\nimport { FavoritesResource } from './resources/favorites'\nimport { ReportsResource } from './resources/reports'\nimport { SupportResource } from './resources/support'\nimport { WebhooksResource } from './resources/webhooks'\nimport { UsageResource } from './resources/usage'\nimport { VERSION } from './version'\n\n/**\n * Configuration options for the GunSpec client.\n *\n * @example\n * ```typescript\n * // Reads GUNSPEC_API_KEY from process.env automatically\n * const client = new GunSpec();\n *\n * // Or pass via environment variable explicitly\n * const client = new GunSpec({\n * apiKey: process.env.GUNSPEC_API_KEY,\n * });\n * ```\n */\nexport interface ClientOptions {\n /**\n * API key for authentication. If not provided, reads from\n * the `GUNSPEC_API_KEY` environment variable.\n */\n apiKey?: string\n\n /**\n * Base URL for the API. Defaults to `https://api.gunspec.io`.\n */\n baseURL?: string\n\n /**\n * Request timeout in milliseconds. Defaults to 30000 (30s).\n */\n timeout?: number\n\n /**\n * Retry configuration for failed requests.\n */\n retry?: RetryConfig\n\n /**\n * Custom default headers to include with every request.\n */\n defaultHeaders?: Record<string, string>\n}\n\n/**\n * GunSpec.io API client — the main entry point for the SDK.\n *\n * Provides typed access to the firearms specification database API\n * through resource-oriented properties that mirror the API structure.\n *\n * @example\n * ```typescript\n * import { GunSpec } from '@gunspec/sdk';\n *\n * // Auto-reads GUNSPEC_API_KEY from environment\n * const client = new GunSpec();\n *\n * // List firearms with filters\n * const { data, pagination } = await client.firearms.list({\n * category: 'pistol',\n * status: 'in_production',\n * per_page: 50,\n * });\n *\n * // Get a specific firearm by slug\n * const { data: glock } = await client.firearms.get('glock-g17');\n *\n * // Search firearms\n * const results = await client.firearms.search({ q: 'beretta 92' });\n *\n * // Compare firearms side by side\n * const { data: comparison } = await client.firearms.compare({\n * ids: 'glock-g17,beretta-92fs,sig-sauer-p226',\n * });\n *\n * // Auto-paginate through all results\n * for await (const firearm of client.firearms.listAutoPaging({ category: 'rifle' })) {\n * console.log(firearm.name);\n * }\n *\n * // Game development features\n * const { data: tierList } = await client.game.tierList({ stat: 'damage' });\n * const { data: matchup } = await client.game.matchups({ a: 'ak-47', b: 'm16a4' });\n *\n * // Statistics\n * const { data: summary } = await client.stats.summary();\n * ```\n */\nexport class GunSpec {\n /** Firearms specifications, search, comparison, and related data */\n readonly firearms: FirearmsResource\n\n /** Firearm manufacturers and their products */\n readonly manufacturers: ManufacturersResource\n\n /** Ammunition calibers and cartridge specifications */\n readonly calibers: CalibersResource\n\n /** Firearm categories (pistol, rifle, shotgun, etc.) */\n readonly categories: CategoriesResource\n\n /** Aggregate statistics across the database */\n readonly stats: StatsResource\n\n /** Game development tools — balance reports, tier lists, matchups */\n readonly game: GameResource\n\n /** Versioned game stats snapshots for pinning game builds */\n readonly gameStats: GameStatsResource\n\n /** Ammunition loads, ballistics, and bullet data */\n readonly ammunition: AmmunitionResource\n\n /** Countries and their military/law enforcement arsenals */\n readonly countries: CountriesResource\n\n /** Armed conflicts and the firearms used in them */\n readonly conflicts: ConflictsResource\n\n /** Data quality metrics (Enterprise tier) */\n readonly dataQuality: DataQualityResource\n\n /** User's favorited firearms */\n readonly favorites: FavoritesResource\n\n /** Data quality reports */\n readonly reports: ReportsResource\n\n /** Support tickets (paid tiers) */\n readonly support: SupportResource\n\n /** Webhook endpoint management (studio+ tier) */\n readonly webhooks: WebhooksResource\n\n /** API usage statistics */\n readonly usage: UsageResource\n\n /** The underlying HTTP client (for advanced use) */\n private readonly _client: HttpClient\n\n constructor(options: ClientOptions = {}) {\n this._client = new HttpClient({\n baseUrl: options.baseURL ?? 'https://api.gunspec.io',\n timeout: options.timeout ?? 30_000,\n auth: {\n apiKey: options.apiKey,\n },\n retry: options.retry,\n headers: {\n ...options.defaultHeaders,\n 'User-Agent': `gunspec-sdk/typescript/${VERSION}`,\n 'X-SDK-Version': VERSION,\n 'X-SDK-Language': 'typescript',\n },\n })\n\n this.firearms = new FirearmsResource(this._client)\n this.manufacturers = new ManufacturersResource(this._client)\n this.calibers = new CalibersResource(this._client)\n this.categories = new CategoriesResource(this._client)\n this.stats = new StatsResource(this._client)\n this.game = new GameResource(this._client)\n this.gameStats = new GameStatsResource(this._client)\n this.ammunition = new AmmunitionResource(this._client)\n this.countries = new CountriesResource(this._client)\n this.conflicts = new ConflictsResource(this._client)\n this.dataQuality = new DataQualityResource(this._client)\n this.favorites = new FavoritesResource(this._client)\n this.reports = new ReportsResource(this._client)\n this.support = new SupportResource(this._client)\n this.webhooks = new WebhooksResource(this._client)\n this.usage = new UsageResource(this._client)\n }\n}\n","/**\n * Pagination utilities for the GunSpec SDK.\n *\n * Provides a {@link Page} wrapper around paginated API responses with\n * convenience methods for navigating between pages and an async iterator\n * for automatic consumption of all pages.\n *\n * @module\n */\n\nimport type { PaginatedResponse, PaginationMeta } from './http-client.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A function that fetches a specific page of results.\n *\n * Used internally by {@link Page} to implement {@link Page.getNextPage} and\n * the async iterator without exposing the full `HttpClient` to consumers.\n *\n * @typeParam T - The element type of the paginated list.\n */\nexport type PageFetcher<T> = (query: Record<string, string | number | boolean | string[] | undefined>) => Promise<PaginatedResponse<T>>;\n\n// ---------------------------------------------------------------------------\n// Page class\n// ---------------------------------------------------------------------------\n\n/**\n * A single page of results from a paginated API endpoint.\n *\n * Wraps the raw {@link PaginatedResponse} and adds navigation helpers:\n *\n * - {@link hasNextPage} — check if more pages are available.\n * - {@link getNextPage} — fetch the next page.\n * - `Symbol.asyncIterator` — iterate over **all** items across all pages.\n *\n * @typeParam T - The type of each item in the page.\n *\n * @example\n * ```ts\n * const page = await client.firearms.list({ page: 1, limit: 25 });\n *\n * // Manual navigation\n * console.log(page.data); // Firearm[]\n * console.log(page.pagination); // { page: 1, limit: 25, total: 400, totalPages: 16 }\n *\n * if (page.hasNextPage()) {\n * const next = await page.getNextPage();\n * }\n *\n * // Automatic iteration over all items\n * for await (const firearm of page) {\n * console.log(firearm.name);\n * }\n * ```\n */\nexport class Page<T> {\n /** The items on this page. */\n readonly data: T[];\n\n /** Pagination metadata returned by the server. */\n readonly pagination: PaginationMeta;\n\n /** The request ID for this page's HTTP response. */\n readonly requestId: string;\n\n /** The base query parameters used to fetch this page (without `page`). */\n private readonly baseQuery: Record<string, string | number | boolean | string[] | undefined>;\n\n /** The fetcher function used to retrieve subsequent pages. */\n private readonly fetcher: PageFetcher<T>;\n\n /**\n * @internal\n * Consumers should not construct `Page` instances directly. Use the\n * resource methods on the high-level client instead.\n */\n constructor(\n response: PaginatedResponse<T>,\n baseQuery: Record<string, string | number | boolean | string[] | undefined>,\n fetcher: PageFetcher<T>,\n ) {\n this.data = response.data;\n this.pagination = response.pagination;\n this.requestId = response.requestId;\n this.baseQuery = baseQuery;\n this.fetcher = fetcher;\n }\n\n /**\n * Whether there is a next page of results.\n *\n * When `totalPages` is available (pro/enterprise tiers) the check is exact.\n * Otherwise the heuristic is: if the current page returned a full page of\n * results (i.e. `data.length === limit`), there is likely another page.\n */\n hasNextPage(): boolean {\n if (this.pagination.totalPages !== undefined) {\n return this.pagination.page < this.pagination.totalPages;\n }\n // Heuristic: if we received a full page, assume there are more.\n return this.data.length >= this.pagination.limit;\n }\n\n /**\n * Fetch the next page of results.\n *\n * @returns A new {@link Page} instance for the next page.\n * @throws {Error} If there is no next page. Always check\n * {@link hasNextPage} first.\n */\n async getNextPage(): Promise<Page<T>> {\n if (!this.hasNextPage()) {\n throw new Error('No more pages available. Check hasNextPage() before calling getNextPage().');\n }\n\n const nextQuery = {\n ...this.baseQuery,\n page: this.pagination.page + 1,\n };\n\n const response = await this.fetcher(nextQuery);\n return new Page<T>(response, this.baseQuery, this.fetcher);\n }\n\n /**\n * Fetch the previous page of results.\n *\n * @returns A new {@link Page} instance for the previous page.\n * @throws {Error} If this is already the first page.\n */\n async getPreviousPage(): Promise<Page<T>> {\n if (this.pagination.page <= 1) {\n throw new Error('Already on the first page. Cannot navigate to a previous page.');\n }\n\n const prevQuery = {\n ...this.baseQuery,\n page: this.pagination.page - 1,\n };\n\n const response = await this.fetcher(prevQuery);\n return new Page<T>(response, this.baseQuery, this.fetcher);\n }\n\n /**\n * Async iterator that yields every item across **all** pages, starting\n * from the current page.\n *\n * Fetches subsequent pages on demand (lazily) so memory usage stays\n * constant regardless of the total result set size.\n *\n * @example\n * ```ts\n * const firstPage = await client.firearms.list({ limit: 50 });\n * for await (const firearm of firstPage) {\n * // Iterates page 1, then auto-fetches page 2, 3, ... until exhausted.\n * console.log(firearm.name);\n * }\n * ```\n */\n async *[Symbol.asyncIterator](): AsyncIterableIterator<T> {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n let current: Page<T> = this;\n\n while (true) {\n for (const item of current.data) {\n yield item;\n }\n\n if (!current.hasNextPage()) {\n break;\n }\n\n current = await current.getNextPage();\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory helper\n// ---------------------------------------------------------------------------\n\n/**\n * Create a {@link Page} from a raw paginated HTTP response.\n *\n * This is the primary factory used by resource classes to wrap their list\n * endpoints.\n *\n * @typeParam T - The element type.\n * @param response - The raw paginated response from {@link HttpClient.requestPaginated}.\n * @param baseQuery - The query parameters used for the request (the `page`\n * key is managed automatically).\n * @param fetcher - A function that fetches a specific page given query\n * parameters.\n * @returns A new {@link Page} instance.\n */\nexport function createPage<T>(\n response: PaginatedResponse<T>,\n baseQuery: Record<string, string | number | boolean | string[] | undefined>,\n fetcher: PageFetcher<T>,\n): Page<T> {\n return new Page(response, baseQuery, fetcher);\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@buun_group/gunspec-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Official TypeScript SDK for the GunSpec.io firearms specification database API",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "LICENSE",
24
+ "README.md"
25
+ ],
26
+ "engines": {
27
+ "node": ">=18"
28
+ },
29
+ "scripts": {
30
+ "build": "tsup",
31
+ "dev": "tsup --watch",
32
+ "test": "vitest run",
33
+ "test:watch": "vitest --watch",
34
+ "typecheck": "tsc --noEmit",
35
+ "clean": "rm -rf dist",
36
+ "prepublishOnly": "npm run build"
37
+ },
38
+ "keywords": [
39
+ "gunspec",
40
+ "firearms",
41
+ "api",
42
+ "sdk",
43
+ "typescript",
44
+ "specifications",
45
+ "database"
46
+ ],
47
+ "author": "BUUN GROUP <hello@buungroup.com>",
48
+ "license": "MIT",
49
+ "homepage": "https://gunspec.io",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "https://gunspec.io",
53
+ "directory": "packages/sdk"
54
+ },
55
+ "bugs": {
56
+ "url": "https://gunspec.io"
57
+ },
58
+ "publishConfig": {
59
+ "access": "public"
60
+ },
61
+ "devDependencies": {
62
+ "@types/node": "^25.2.3",
63
+ "tsup": "^8.0.0",
64
+ "typescript": "^5.7.0",
65
+ "vitest": "^3.0.0"
66
+ }
67
+ }