@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,4554 @@
1
+ /**
2
+ * Retry logic with exponential backoff for the GunSpec SDK.
3
+ *
4
+ * Only **idempotent** HTTP methods (`GET`, `PUT`, `DELETE`) are retried
5
+ * automatically. Transient failures (timeouts, 429, 5xx) trigger a retry
6
+ * after an exponentially increasing delay with +-20 % jitter.
7
+ *
8
+ * @module
9
+ */
10
+ /**
11
+ * Configuration for the retry behaviour.
12
+ *
13
+ * All fields are optional and fall back to sensible defaults.
14
+ */
15
+ interface RetryConfig {
16
+ /**
17
+ * Maximum number of retry attempts (excluding the initial request).
18
+ *
19
+ * Set to `0` to disable retries entirely.
20
+ *
21
+ * @defaultValue `2`
22
+ */
23
+ maxRetries?: number;
24
+ /**
25
+ * Initial delay in milliseconds before the first retry.
26
+ *
27
+ * @defaultValue `500`
28
+ */
29
+ initialDelayMs?: number;
30
+ /**
31
+ * Upper-bound delay in milliseconds. The computed delay is capped at this
32
+ * value regardless of how many retries have occurred.
33
+ *
34
+ * @defaultValue `8000`
35
+ */
36
+ maxDelayMs?: number;
37
+ /**
38
+ * Multiplier applied to the delay after each retry.
39
+ *
40
+ * @defaultValue `2`
41
+ */
42
+ multiplier?: number;
43
+ }
44
+ /** Fully resolved retry configuration with all defaults applied. */
45
+ interface ResolvedRetryConfig {
46
+ readonly maxRetries: number;
47
+ readonly initialDelayMs: number;
48
+ readonly maxDelayMs: number;
49
+ readonly multiplier: number;
50
+ }
51
+
52
+ /**
53
+ * Authentication handler for the GunSpec SDK.
54
+ *
55
+ * Resolves an API key from an explicit option or the `GUNSPEC_API_KEY`
56
+ * environment variable, and produces the header map needed for every
57
+ * authenticated request.
58
+ *
59
+ * @module
60
+ */
61
+ /**
62
+ * Options accepted by the authentication handler.
63
+ */
64
+ interface AuthConfig {
65
+ /**
66
+ * An explicit API key to use for all requests.
67
+ *
68
+ * When omitted the handler falls back to the `GUNSPEC_API_KEY` environment
69
+ * variable (Node.js / Deno / Bun `process.env`). If neither is available
70
+ * the SDK operates in anonymous mode (no auth header is sent).
71
+ */
72
+ apiKey?: string;
73
+ }
74
+ /**
75
+ * Resolve the API key to use for authentication.
76
+ *
77
+ * Resolution order:
78
+ * 1. Explicit `apiKey` option passed to the constructor.
79
+ * 2. `GUNSPEC_API_KEY` environment variable.
80
+ * 3. `undefined` (anonymous / unauthenticated).
81
+ *
82
+ * @param config - Authentication configuration.
83
+ * @returns The resolved API key, or `undefined` if none is available.
84
+ */
85
+ declare function resolveApiKey(config: AuthConfig): string | undefined;
86
+ /**
87
+ * Build the authentication headers for a request.
88
+ *
89
+ * If an API key is available it is sent via the `X-API-Key` header.
90
+ * Returns an empty object when no key is available so the request proceeds
91
+ * in anonymous mode.
92
+ *
93
+ * @remarks
94
+ * The key value is **never** logged or serialised to avoid accidental
95
+ * credential leakage.
96
+ *
97
+ * @param apiKey - The resolved API key (may be `undefined`).
98
+ * @returns A header record to merge into the outgoing request.
99
+ */
100
+ declare function buildAuthHeaders(apiKey: string | undefined): Record<string, string>;
101
+
102
+ /**
103
+ * HTTP transport layer for the GunSpec SDK.
104
+ *
105
+ * Uses the native `fetch` API (available in Node 18+, Deno, Bun, Cloudflare
106
+ * Workers, and modern browsers) with zero external dependencies.
107
+ *
108
+ * @module
109
+ */
110
+
111
+ /**
112
+ * Configuration accepted by the {@link HttpClient} constructor.
113
+ */
114
+ interface HttpClientConfig {
115
+ /**
116
+ * Base URL for all API requests.
117
+ *
118
+ * Trailing slashes are stripped automatically.
119
+ *
120
+ * @defaultValue `"https://api.gunspec.io"`
121
+ */
122
+ baseUrl?: string;
123
+ /**
124
+ * Default request timeout in milliseconds.
125
+ *
126
+ * Individual requests can override this via {@link RequestConfig.timeout}.
127
+ *
128
+ * @defaultValue `30_000` (30 seconds)
129
+ */
130
+ timeout?: number;
131
+ /**
132
+ * Extra headers merged into every outgoing request.
133
+ *
134
+ * Useful for setting a custom `User-Agent` or forwarding correlation IDs.
135
+ */
136
+ headers?: Record<string, string>;
137
+ /** Authentication options. */
138
+ auth?: AuthConfig;
139
+ /** Retry / backoff options. */
140
+ retry?: RetryConfig;
141
+ }
142
+ /**
143
+ * Describes a single HTTP request to be executed by the client.
144
+ */
145
+ interface RequestConfig {
146
+ /** HTTP method. */
147
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
148
+ /**
149
+ * URL path **relative to the base URL**.
150
+ *
151
+ * Must start with `/` (e.g. `/v1/firearms`).
152
+ */
153
+ path: string;
154
+ /**
155
+ * Query string parameters.
156
+ *
157
+ * - `undefined` values are silently omitted.
158
+ * - Arrays are serialised as repeated keys (`caliber=9mm&caliber=.45`).
159
+ * - Booleans are stringified (`"true"` / `"false"`).
160
+ */
161
+ query?: Record<string, string | number | boolean | string[] | undefined>;
162
+ /** JSON request body (automatically stringified). */
163
+ body?: unknown;
164
+ /** Per-request header overrides. */
165
+ headers?: Record<string, string>;
166
+ /**
167
+ * Per-request timeout in milliseconds.
168
+ *
169
+ * Overrides the client-level default.
170
+ */
171
+ timeout?: number;
172
+ /**
173
+ * An external `AbortSignal` that the caller can use to cancel the request.
174
+ *
175
+ * This signal is composed with the internal timeout signal so that either
176
+ * mechanism can abort the request.
177
+ */
178
+ signal?: AbortSignal;
179
+ }
180
+ /**
181
+ * Unwrapped API response for endpoints that return a single resource.
182
+ *
183
+ * The SDK strips the `{ success, data }` envelope and hoists `data` to the
184
+ * top level.
185
+ */
186
+ interface APIResponse<T> {
187
+ /** The unwrapped response payload. */
188
+ readonly data: T;
189
+ /** HTTP status code. */
190
+ readonly status: number;
191
+ /** Raw response headers. */
192
+ readonly headers: Headers;
193
+ /** The `X-Request-Id` response header. */
194
+ readonly requestId: string;
195
+ /** Parsed rate limit headers. */
196
+ readonly rateLimit: RateLimitInfo;
197
+ }
198
+ /**
199
+ * Unwrapped API response for endpoints that return a paginated list.
200
+ */
201
+ interface PaginatedResponse<T> {
202
+ /** Array of resource objects. */
203
+ readonly data: T[];
204
+ /** Pagination metadata returned by the server. */
205
+ readonly pagination: PaginationMeta;
206
+ /** HTTP status code. */
207
+ readonly status: number;
208
+ /** Raw response headers. */
209
+ readonly headers: Headers;
210
+ /** The `X-Request-Id` response header. */
211
+ readonly requestId: string;
212
+ /** Parsed rate limit headers. */
213
+ readonly rateLimit: RateLimitInfo;
214
+ }
215
+ /**
216
+ * Pagination metadata included in list responses.
217
+ */
218
+ interface PaginationMeta {
219
+ /** Current page number (1-indexed). */
220
+ readonly page: number;
221
+ /** Maximum number of items per page. */
222
+ readonly limit: number;
223
+ /** Total number of matching items (omitted for anonymous/free tiers). */
224
+ readonly total?: number;
225
+ /** Total number of pages (omitted when `total` is omitted). */
226
+ readonly totalPages?: number;
227
+ }
228
+ /**
229
+ * Rate limit information parsed from response headers.
230
+ */
231
+ interface RateLimitInfo {
232
+ /** Maximum requests allowed per window, or `null` if the header is absent. */
233
+ readonly limit: number | null;
234
+ /** Requests remaining in the current window, or `null`. */
235
+ readonly remaining: number | null;
236
+ /** Unix timestamp (seconds) when the window resets, or `null`. */
237
+ readonly reset: number | null;
238
+ }
239
+ /**
240
+ * Low-level HTTP client for the GunSpec API.
241
+ *
242
+ * Handles URL construction, query string serialisation, authentication
243
+ * headers, timeout management, response envelope unwrapping, error mapping,
244
+ * and automatic retries with exponential backoff.
245
+ *
246
+ * Most consumers should use the high-level `GunSpec` client class instead,
247
+ * which delegates to an `HttpClient` instance internally.
248
+ *
249
+ * @example
250
+ * ```ts
251
+ * const http = new HttpClient({ auth: { apiKey: process.env.GUNSPEC_API_KEY } });
252
+ * const res = await http.request<Firearm>({
253
+ * method: 'GET',
254
+ * path: '/v1/firearms/glock-g17',
255
+ * });
256
+ * console.log(res.data.name); // "Glock G17"
257
+ * ```
258
+ */
259
+ declare class HttpClient {
260
+ private readonly baseUrl;
261
+ private readonly defaultTimeout;
262
+ private readonly defaultHeaders;
263
+ private readonly apiKey;
264
+ private readonly retryConfig;
265
+ constructor(config?: HttpClientConfig);
266
+ /**
267
+ * Execute an HTTP request and return the unwrapped response.
268
+ *
269
+ * The API's `{ success: true, data: T }` envelope is stripped so that
270
+ * callers receive `T` directly via {@link APIResponse.data}.
271
+ *
272
+ * @typeParam T - The expected type of the `data` field in the response.
273
+ * @param config - Request configuration.
274
+ * @returns The unwrapped API response.
275
+ * @throws {@link APIError} on non-2xx responses.
276
+ * @throws {@link ConnectionError} on network failures.
277
+ * @throws {@link TimeoutError} when the request exceeds the timeout.
278
+ */
279
+ request<T>(config: RequestConfig): Promise<APIResponse<T>>;
280
+ /**
281
+ * Execute an HTTP request and return a paginated response.
282
+ *
283
+ * The API's `{ success: true, data: T[], pagination }` envelope is
284
+ * unwrapped into a {@link PaginatedResponse}.
285
+ *
286
+ * @typeParam T - The element type of the paginated list.
287
+ * @param config - Request configuration.
288
+ * @returns The unwrapped paginated response.
289
+ * @throws {@link APIError} on non-2xx responses.
290
+ * @throws {@link ConnectionError} on network failures.
291
+ * @throws {@link TimeoutError} when the request exceeds the timeout.
292
+ */
293
+ requestPaginated<T>(config: RequestConfig): Promise<PaginatedResponse<T>>;
294
+ /**
295
+ * Convenience wrapper for a GET request returning a single resource.
296
+ */
297
+ get<T>(path: string, query?: Record<string, any>): Promise<APIResponse<T>>;
298
+ /**
299
+ * Convenience wrapper for a GET request returning a paginated list.
300
+ */
301
+ getPaginated<T>(path: string, query?: Record<string, any>): Promise<PaginatedResponse<T>>;
302
+ /**
303
+ * Convenience wrapper for a POST request.
304
+ */
305
+ post<T>(path: string, body?: unknown, query?: Record<string, any>): Promise<APIResponse<T>>;
306
+ /**
307
+ * Convenience wrapper for a PUT request.
308
+ */
309
+ put<T>(path: string, body?: unknown, query?: Record<string, any>): Promise<APIResponse<T>>;
310
+ /**
311
+ * Convenience wrapper for a DELETE request.
312
+ */
313
+ delete<T>(path: string, query?: Record<string, any>): Promise<APIResponse<T>>;
314
+ private executeRequest;
315
+ private executePaginatedRequest;
316
+ /**
317
+ * Perform the raw `fetch` call with merged headers, query string, timeout,
318
+ * and body serialisation.
319
+ */
320
+ private doFetch;
321
+ /**
322
+ * Build the full URL from base URL, path, and query parameters.
323
+ */
324
+ private buildUrl;
325
+ /**
326
+ * Parse an error response body and throw the appropriate {@link APIError}.
327
+ */
328
+ private throwAPIError;
329
+ }
330
+
331
+ /**
332
+ * Pagination utilities for the GunSpec SDK.
333
+ *
334
+ * Provides a {@link Page} wrapper around paginated API responses with
335
+ * convenience methods for navigating between pages and an async iterator
336
+ * for automatic consumption of all pages.
337
+ *
338
+ * @module
339
+ */
340
+
341
+ /**
342
+ * A function that fetches a specific page of results.
343
+ *
344
+ * Used internally by {@link Page} to implement {@link Page.getNextPage} and
345
+ * the async iterator without exposing the full `HttpClient` to consumers.
346
+ *
347
+ * @typeParam T - The element type of the paginated list.
348
+ */
349
+ type PageFetcher<T> = (query: Record<string, string | number | boolean | string[] | undefined>) => Promise<PaginatedResponse<T>>;
350
+ /**
351
+ * A single page of results from a paginated API endpoint.
352
+ *
353
+ * Wraps the raw {@link PaginatedResponse} and adds navigation helpers:
354
+ *
355
+ * - {@link hasNextPage} — check if more pages are available.
356
+ * - {@link getNextPage} — fetch the next page.
357
+ * - `Symbol.asyncIterator` — iterate over **all** items across all pages.
358
+ *
359
+ * @typeParam T - The type of each item in the page.
360
+ *
361
+ * @example
362
+ * ```ts
363
+ * const page = await client.firearms.list({ page: 1, limit: 25 });
364
+ *
365
+ * // Manual navigation
366
+ * console.log(page.data); // Firearm[]
367
+ * console.log(page.pagination); // { page: 1, limit: 25, total: 400, totalPages: 16 }
368
+ *
369
+ * if (page.hasNextPage()) {
370
+ * const next = await page.getNextPage();
371
+ * }
372
+ *
373
+ * // Automatic iteration over all items
374
+ * for await (const firearm of page) {
375
+ * console.log(firearm.name);
376
+ * }
377
+ * ```
378
+ */
379
+ declare class Page<T> {
380
+ /** The items on this page. */
381
+ readonly data: T[];
382
+ /** Pagination metadata returned by the server. */
383
+ readonly pagination: PaginationMeta;
384
+ /** The request ID for this page's HTTP response. */
385
+ readonly requestId: string;
386
+ /** The base query parameters used to fetch this page (without `page`). */
387
+ private readonly baseQuery;
388
+ /** The fetcher function used to retrieve subsequent pages. */
389
+ private readonly fetcher;
390
+ /**
391
+ * @internal
392
+ * Consumers should not construct `Page` instances directly. Use the
393
+ * resource methods on the high-level client instead.
394
+ */
395
+ constructor(response: PaginatedResponse<T>, baseQuery: Record<string, string | number | boolean | string[] | undefined>, fetcher: PageFetcher<T>);
396
+ /**
397
+ * Whether there is a next page of results.
398
+ *
399
+ * When `totalPages` is available (pro/enterprise tiers) the check is exact.
400
+ * Otherwise the heuristic is: if the current page returned a full page of
401
+ * results (i.e. `data.length === limit`), there is likely another page.
402
+ */
403
+ hasNextPage(): boolean;
404
+ /**
405
+ * Fetch the next page of results.
406
+ *
407
+ * @returns A new {@link Page} instance for the next page.
408
+ * @throws {Error} If there is no next page. Always check
409
+ * {@link hasNextPage} first.
410
+ */
411
+ getNextPage(): Promise<Page<T>>;
412
+ /**
413
+ * Fetch the previous page of results.
414
+ *
415
+ * @returns A new {@link Page} instance for the previous page.
416
+ * @throws {Error} If this is already the first page.
417
+ */
418
+ getPreviousPage(): Promise<Page<T>>;
419
+ /**
420
+ * Async iterator that yields every item across **all** pages, starting
421
+ * from the current page.
422
+ *
423
+ * Fetches subsequent pages on demand (lazily) so memory usage stays
424
+ * constant regardless of the total result set size.
425
+ *
426
+ * @example
427
+ * ```ts
428
+ * const firstPage = await client.firearms.list({ limit: 50 });
429
+ * for await (const firearm of firstPage) {
430
+ * // Iterates page 1, then auto-fetches page 2, 3, ... until exhausted.
431
+ * console.log(firearm.name);
432
+ * }
433
+ * ```
434
+ */
435
+ [Symbol.asyncIterator](): AsyncIterableIterator<T>;
436
+ }
437
+ /**
438
+ * Create a {@link Page} from a raw paginated HTTP response.
439
+ *
440
+ * This is the primary factory used by resource classes to wrap their list
441
+ * endpoints.
442
+ *
443
+ * @typeParam T - The element type.
444
+ * @param response - The raw paginated response from {@link HttpClient.requestPaginated}.
445
+ * @param baseQuery - The query parameters used for the request (the `page`
446
+ * key is managed automatically).
447
+ * @param fetcher - A function that fetches a specific page given query
448
+ * parameters.
449
+ * @returns A new {@link Page} instance.
450
+ */
451
+ declare function createPage<T>(response: PaginatedResponse<T>, baseQuery: Record<string, string | number | boolean | string[] | undefined>, fetcher: PageFetcher<T>): Page<T>;
452
+
453
+ /**
454
+ * Error hierarchy for the GunSpec SDK.
455
+ *
456
+ * All SDK errors extend {@link GunSpecError} so consumers can catch them
457
+ * uniformly with a single `catch (e) { if (e instanceof GunSpecError) … }`.
458
+ *
459
+ * HTTP errors returned by the API are mapped to specific subclasses of
460
+ * {@link APIError} via the {@link createAPIError} factory function.
461
+ *
462
+ * @module
463
+ */
464
+ /**
465
+ * Base error class for every error thrown by the GunSpec SDK.
466
+ *
467
+ * @remarks
468
+ * Restores the prototype chain so `instanceof` checks work correctly even
469
+ * when transpiled to ES5.
470
+ */
471
+ declare class GunSpecError extends Error {
472
+ readonly name: string;
473
+ constructor(message: string);
474
+ }
475
+ /**
476
+ * An error returned by the GunSpec API with an HTTP status code.
477
+ *
478
+ * Subclasses are created for well-known status codes (401, 403, 404, …).
479
+ * For any other status code the base `APIError` is thrown directly.
480
+ */
481
+ declare class APIError extends GunSpecError {
482
+ readonly name: string;
483
+ /** HTTP status code returned by the API. */
484
+ readonly status: number;
485
+ /** Machine-readable error code from the response body (e.g. `"NOT_FOUND"`). */
486
+ readonly code: string;
487
+ /** The `X-Request-Id` header value, useful for support requests. */
488
+ readonly requestId: string;
489
+ /** Raw response headers for further inspection. */
490
+ readonly headers: Headers;
491
+ constructor(status: number, code: string, message: string, requestId: string, headers: Headers);
492
+ }
493
+ /**
494
+ * Thrown when the API returns **401 Unauthorized**.
495
+ *
496
+ * Usually means the API key is missing, invalid, or expired.
497
+ */
498
+ declare class AuthenticationError extends APIError {
499
+ readonly name: string;
500
+ constructor(code: string, message: string, requestId: string, headers: Headers);
501
+ }
502
+ /**
503
+ * Thrown when the API returns **403 Forbidden**.
504
+ *
505
+ * The API key is valid but the associated tier does not have access to the
506
+ * requested resource or action.
507
+ */
508
+ declare class PermissionError extends APIError {
509
+ readonly name: string;
510
+ constructor(code: string, message: string, requestId: string, headers: Headers);
511
+ }
512
+ /**
513
+ * Thrown when the API returns **404 Not Found**.
514
+ */
515
+ declare class NotFoundError extends APIError {
516
+ readonly name: string;
517
+ constructor(code: string, message: string, requestId: string, headers: Headers);
518
+ }
519
+ /**
520
+ * Thrown when the API returns **400 Bad Request**.
521
+ *
522
+ * Typically caused by invalid query parameters or a malformed request body.
523
+ */
524
+ declare class BadRequestError extends APIError {
525
+ readonly name: string;
526
+ constructor(code: string, message: string, requestId: string, headers: Headers);
527
+ }
528
+ /**
529
+ * Thrown when the API returns **429 Too Many Requests**.
530
+ *
531
+ * The {@link retryAfter} property contains the number of seconds to wait
532
+ * before retrying, parsed from the `Retry-After` response header.
533
+ */
534
+ declare class RateLimitError extends APIError {
535
+ readonly name: string;
536
+ /**
537
+ * Number of seconds the client should wait before retrying, or `null` if
538
+ * the server did not provide a `Retry-After` header.
539
+ */
540
+ readonly retryAfter: number | null;
541
+ constructor(code: string, message: string, requestId: string, headers: Headers, retryAfter: number | null);
542
+ }
543
+ /**
544
+ * Thrown when the API returns **500 Internal Server Error**.
545
+ */
546
+ declare class InternalServerError extends APIError {
547
+ readonly name: string;
548
+ constructor(code: string, message: string, requestId: string, headers: Headers);
549
+ }
550
+ /**
551
+ * Thrown when a network-level failure prevents the request from completing.
552
+ *
553
+ * This covers DNS resolution errors, connection resets, and any other
554
+ * `TypeError` thrown by the native `fetch` implementation.
555
+ */
556
+ declare class ConnectionError extends GunSpecError {
557
+ readonly name: string;
558
+ /** The original error thrown by `fetch`. */
559
+ readonly cause: unknown;
560
+ constructor(message: string, cause: unknown);
561
+ }
562
+ /**
563
+ * Thrown when a request exceeds the configured timeout.
564
+ *
565
+ * The timeout is implemented via `AbortController` and fires when the
566
+ * elapsed time exceeds `HttpClientConfig.timeout`.
567
+ */
568
+ declare class TimeoutError extends GunSpecError {
569
+ readonly name: string;
570
+ /** The timeout duration in milliseconds that was exceeded. */
571
+ readonly timeoutMs: number;
572
+ constructor(timeoutMs: number);
573
+ }
574
+ /**
575
+ * Error envelope returned by the GunSpec API on non-2xx responses.
576
+ */
577
+ interface ErrorBody$1 {
578
+ success: false;
579
+ error: {
580
+ code: string;
581
+ message: string;
582
+ };
583
+ }
584
+ /**
585
+ * Create the appropriate {@link APIError} subclass for the given HTTP status
586
+ * code and response body.
587
+ *
588
+ * @param status - HTTP status code.
589
+ * @param body - Parsed JSON body (may be `null` if the body was empty or
590
+ * unparseable).
591
+ * @param requestId - Value of the `X-Request-Id` response header.
592
+ * @param headers - Raw response headers.
593
+ * @returns A concrete {@link APIError} subclass instance.
594
+ */
595
+ declare function createAPIError(status: number, body: ErrorBody$1 | null, requestId: string, headers: Headers): APIError;
596
+
597
+ /**
598
+ * A firearm specification record.
599
+ *
600
+ * The full detail shape is returned by `GET /v1/firearms/:id`.
601
+ * List endpoints return a subset of these fields.
602
+ *
603
+ * @example
604
+ * ```ts
605
+ * const glock: Firearm = {
606
+ * id: 'glock-g17',
607
+ * name: 'Glock G17',
608
+ * manufacturerId: 'glock',
609
+ * categoryId: 'semi-automatic-pistol',
610
+ * status: 'in_production',
611
+ * yearIntroduced: 1982,
612
+ * countryOfOrigin: 'AT',
613
+ * actionType: 'short_recoil',
614
+ * };
615
+ * ```
616
+ */
617
+ interface Firearm {
618
+ /** URL-safe slug identifier (e.g. `"glock-g17"`). */
619
+ readonly id: string;
620
+ /** Display name of the firearm. */
621
+ name: string;
622
+ /** Slug of the manufacturer (FK to {@link Manufacturer.id}). */
623
+ manufacturerId: string;
624
+ /** Slug of the category (FK to {@link Category.id}). */
625
+ categoryId: string;
626
+ /** Slug of the parent firearm when this is a variant. */
627
+ parentFirearmId?: string | null;
628
+ /** Free-text label describing the variant relationship (e.g. `"compact"`, `"tactical"`). */
629
+ variantType?: string | null;
630
+ /** Year the firearm was first produced. */
631
+ yearIntroduced?: number | null;
632
+ /** Year production ended, if applicable. */
633
+ yearDiscontinued?: number | null;
634
+ /** Current production status. */
635
+ status?: 'in_production' | 'discontinued' | 'prototype' | null;
636
+ /** ISO 3166-1 alpha-2 country code of origin (e.g. `"US"`, `"AT"`). */
637
+ countryOfOrigin?: string | null;
638
+ /** Empty weight in grams. */
639
+ weightEmptyG?: number | null;
640
+ /** Loaded weight in grams. */
641
+ weightLoadedG?: number | null;
642
+ /** Overall length in millimetres. */
643
+ overallLengthMm?: number | null;
644
+ /** Barrel length in millimetres. */
645
+ barrelLengthMm?: number | null;
646
+ /** Height in millimetres. */
647
+ heightMm?: number | null;
648
+ /** Width in millimetres. */
649
+ widthMm?: number | null;
650
+ /** Sight radius in millimetres. */
651
+ sightRadiusMm?: number | null;
652
+ /** Folded/collapsed length in millimetres, if applicable. */
653
+ foldedLengthMm?: number | null;
654
+ /** Action type slug (e.g. `"short_recoil"`, `"gas_operated"`). */
655
+ actionType?: string | null;
656
+ /** Firing mechanism description. */
657
+ firingMechanism?: string | null;
658
+ /** Trigger type (e.g. `"single_action"`, `"double_action"`). */
659
+ triggerType?: string | null;
660
+ /** Trigger pull force in newtons. */
661
+ triggerPullN?: number | null;
662
+ /** Standard magazine capacity in rounds. */
663
+ magazineCapacity?: number | null;
664
+ /** Magazine type description. */
665
+ magazineType?: string | null;
666
+ /** Muzzle velocity in metres per second. */
667
+ muzzleVelocityMps?: number | null;
668
+ /** Muzzle energy in joules. */
669
+ muzzleEnergyJ?: number | null;
670
+ /** Effective range in metres. */
671
+ effectiveRangeM?: number | null;
672
+ /** Maximum range in metres. */
673
+ maxRangeM?: number | null;
674
+ /** Cyclic rate of fire in rounds per minute. */
675
+ rateOfFireRpm?: number | null;
676
+ /** Barrel rifling description (e.g. `"polygonal"`, `"conventional"`). */
677
+ barrelRifling?: string | null;
678
+ /** Rifling twist rate in millimetres per revolution. */
679
+ riflingTwistMm?: number | null;
680
+ /** Number of rifling grooves. */
681
+ numberOfGrooves?: number | null;
682
+ /** Frame/receiver material. */
683
+ frameMaterial?: string | null;
684
+ /** Slide material. */
685
+ slideMaterial?: string | null;
686
+ /** Barrel material. */
687
+ barrelMaterial?: string | null;
688
+ /** Stock material. */
689
+ stockMaterial?: string | null;
690
+ /** Surface finish description. */
691
+ finish?: string | null;
692
+ /** Safety mechanism descriptions. */
693
+ safetyMechanisms?: string | null;
694
+ /** Notable features. */
695
+ features?: string | null;
696
+ /** Feed system descriptions. */
697
+ feedSystems?: string | null;
698
+ /** Alternative names / designations. */
699
+ alternateNames?: string | null;
700
+ /** Available firing modes (e.g. `["semi-automatic","full-automatic"]`). */
701
+ firingModes?: string | null;
702
+ /** Conflicts this firearm was used in (JSON array of objects). */
703
+ conflicts?: string | null;
704
+ /** Production numbers (JSON object). */
705
+ productionNumbers?: string | null;
706
+ /** Sources / references (JSON array). */
707
+ sources?: string | null;
708
+ /** Game damage rating (0-100). */
709
+ gameDamage?: number | null;
710
+ /** Game accuracy rating (0-100). */
711
+ gameAccuracy?: number | null;
712
+ /** Game range rating (0-100). */
713
+ gameRange?: number | null;
714
+ /** Game fire rate rating (0-100). */
715
+ gameFireRate?: number | null;
716
+ /** Game mobility rating (0-100). */
717
+ gameMobility?: number | null;
718
+ /** Game recoil control rating (0-100). */
719
+ gameRecoilControl?: number | null;
720
+ /** Game reload speed rating (0-100). */
721
+ gameReloadSpeed?: number | null;
722
+ /** Game concealment rating (0-100). */
723
+ gameConcealment?: number | null;
724
+ /** Long-form description / history. */
725
+ description?: string | null;
726
+ /** Editorial notes. */
727
+ notes?: string | null;
728
+ /** Firearm designer(s). */
729
+ designer?: string | null;
730
+ /** Historical lore / trivia text. */
731
+ lore?: string | null;
732
+ /** Source-reported muzzle velocity in m/s. */
733
+ sourceMuzzleVelocityMps?: number | null;
734
+ /** Source-reported muzzle energy in joules. */
735
+ sourceMuzzleEnergyJ?: number | null;
736
+ /** Source-reported effective range in metres. */
737
+ sourceEffectiveRangeM?: number | null;
738
+ /** Source-reported maximum range in metres. */
739
+ sourceMaxRangeM?: number | null;
740
+ /** Ballistics data source name. */
741
+ ballisticsSource?: string | null;
742
+ /** URL for ballistics data source. */
743
+ ballisticsSourceUrl?: string | null;
744
+ /** Default ammunition load slug. */
745
+ defaultAmmoId?: string | null;
746
+ /** Whether a 3-D model is available (`0` or `1`). */
747
+ has3dModel?: number | null;
748
+ /** URL to the SVG line-art silhouette. */
749
+ svgLineArtUrl?: string | null;
750
+ /** URL to the 3-D model asset (glTF / GLB). */
751
+ model3dUrl?: string | null;
752
+ /** Confidence score for the data (0.0 - 1.0). */
753
+ dataConfidence?: number | null;
754
+ /** ISO-8601 creation timestamp. */
755
+ readonly createdAt: string;
756
+ /** ISO-8601 last-update timestamp. */
757
+ readonly updatedAt: string;
758
+ }
759
+ /**
760
+ * Full firearm detail including related entities.
761
+ *
762
+ * Returned by `GET /v1/firearms/:id`.
763
+ */
764
+ interface FirearmDetail extends Firearm {
765
+ /** The manufacturer record. */
766
+ manufacturer: Manufacturer | null;
767
+ /** The category record. */
768
+ category: Category | null;
769
+ /** Calibers this firearm chambers. */
770
+ calibers: FirearmCaliberEntry[];
771
+ /** Associated images. */
772
+ images: FirearmImage[];
773
+ /** Known military / law-enforcement users. */
774
+ users: FirearmUser[];
775
+ }
776
+ /**
777
+ * A caliber entry on a firearm detail, joined from the junction table.
778
+ */
779
+ interface FirearmCaliberEntry {
780
+ /** Caliber slug. */
781
+ caliberId: string;
782
+ /** Whether this is the primary chambering (`1`) or alternate (`0`). */
783
+ isPrimary: number;
784
+ /** Caliber display name. */
785
+ name: string;
786
+ /** NATO designation, if any. */
787
+ natoDesignation?: string | null;
788
+ /** Bullet diameter in mm. */
789
+ bulletDiameterMm?: number | null;
790
+ /** Case length in mm. */
791
+ caseLengthMm?: number | null;
792
+ }
793
+ /**
794
+ * A firearms manufacturer.
795
+ *
796
+ * @example
797
+ * ```ts
798
+ * const glock: Manufacturer = {
799
+ * id: 'glock',
800
+ * name: 'Glock',
801
+ * countryCode: 'AT',
802
+ * foundedYear: 1963,
803
+ * createdAt: '2024-01-01T00:00:00',
804
+ * updatedAt: '2024-01-01T00:00:00',
805
+ * };
806
+ * ```
807
+ */
808
+ interface Manufacturer {
809
+ /** URL-safe slug identifier. */
810
+ readonly id: string;
811
+ /** Display name. */
812
+ name: string;
813
+ /** ISO 3166-1 alpha-2 country code. */
814
+ countryCode?: string | null;
815
+ /** Year the company was founded. */
816
+ foundedYear?: number | null;
817
+ /** Company website URL. */
818
+ website?: string | null;
819
+ /** URL to the company logo. */
820
+ logoUrl?: string | null;
821
+ /** Long-form description. */
822
+ description?: string | null;
823
+ /** ISO-8601 creation timestamp. */
824
+ readonly createdAt: string;
825
+ /** ISO-8601 last-update timestamp. */
826
+ readonly updatedAt: string;
827
+ }
828
+ /**
829
+ * Per-manufacturer aggregate statistics.
830
+ *
831
+ * Returned by `GET /v1/manufacturers/:id/stats`.
832
+ */
833
+ interface ManufacturerStats {
834
+ /** The manufacturer summary. */
835
+ manufacturer: {
836
+ id: string;
837
+ name: string;
838
+ };
839
+ /** Aggregate statistics. */
840
+ stats: {
841
+ total_firearms: number;
842
+ avg_weight_g: number | null;
843
+ avg_range_m: number | null;
844
+ avg_capacity: number | null;
845
+ active_count: number;
846
+ discontinued_count: number;
847
+ earliest_year: number | null;
848
+ latest_year: number | null;
849
+ } | null;
850
+ /** Breakdown by category. */
851
+ categories: Array<{
852
+ id: string;
853
+ name: string;
854
+ count: number;
855
+ }>;
856
+ /** The most common caliber among this manufacturer's firearms. */
857
+ mostCommonCaliber: {
858
+ id: string;
859
+ name: string;
860
+ count: number;
861
+ } | null;
862
+ }
863
+ /**
864
+ * Manufacturer timeline grouping firearms by year introduced.
865
+ *
866
+ * Returned by `GET /v1/manufacturers/:id/timeline`.
867
+ */
868
+ interface ManufacturerTimeline {
869
+ /** The manufacturer summary. */
870
+ manufacturer: {
871
+ id: string;
872
+ name: string;
873
+ };
874
+ /** Firearms grouped by year. */
875
+ timeline: Array<{
876
+ year: number | null;
877
+ firearms: Array<{
878
+ id: string;
879
+ name: string;
880
+ yearIntroduced: number | null;
881
+ categoryId: string;
882
+ status: string | null;
883
+ }>;
884
+ }>;
885
+ }
886
+ /**
887
+ * A cartridge / caliber specification.
888
+ *
889
+ * @example
890
+ * ```ts
891
+ * const nato556: Caliber = {
892
+ * id: '5-56x45mm-nato',
893
+ * name: '5.56x45mm NATO',
894
+ * cartridgeType: 'centerfire_rifle',
895
+ * bulletDiameterMm: 5.70,
896
+ * createdAt: '2024-01-01T00:00:00',
897
+ * updatedAt: '2024-01-01T00:00:00',
898
+ * };
899
+ * ```
900
+ */
901
+ interface Caliber {
902
+ /** URL-safe slug identifier. */
903
+ readonly id: string;
904
+ /** Display name. */
905
+ name: string;
906
+ /** JSON array of alternative names (stored as text). */
907
+ aliases?: string | null;
908
+ /** NATO designation (e.g. `"5.56x45mm NATO"`). */
909
+ natoDesignation?: string | null;
910
+ /** Bullet diameter in millimetres. */
911
+ bulletDiameterMm?: number | null;
912
+ /** Neck diameter in millimetres. */
913
+ neckDiameterMm?: number | null;
914
+ /** Base diameter in millimetres. */
915
+ baseDiameterMm?: number | null;
916
+ /** Case length in millimetres. */
917
+ caseLengthMm?: number | null;
918
+ /** Overall cartridge length in millimetres. */
919
+ overallLengthMm?: number | null;
920
+ /** Maximum chamber pressure in megapascals. */
921
+ maxPressureMpa?: number | null;
922
+ /** Maximum chamber pressure in PSI. */
923
+ maxPressurePsi?: number | null;
924
+ /** Typical bullet weight in grams. */
925
+ typicalBulletWeightG?: number | null;
926
+ /** Typical muzzle velocity in m/s. */
927
+ typicalMuzzleVelocityMps?: number | null;
928
+ /** Typical muzzle energy in joules. */
929
+ typicalMuzzleEnergyJ?: number | null;
930
+ /** Primer type (e.g. `"Boxer"`, `"Berdan"`). */
931
+ primerType?: string | null;
932
+ /** Cartridge type (e.g. `"centerfire_rifle"`, `"rimfire"`). */
933
+ cartridgeType?: string | null;
934
+ /** Slug of the parent cartridge, if this is a derivative. */
935
+ parentCartridgeId?: string | null;
936
+ /** Year the cartridge was introduced. */
937
+ yearIntroduced?: number | null;
938
+ /** Cartridge designer. */
939
+ designer?: string | null;
940
+ /** ISO-8601 creation timestamp. */
941
+ readonly createdAt: string;
942
+ /** ISO-8601 last-update timestamp. */
943
+ readonly updatedAt: string;
944
+ }
945
+ /**
946
+ * A specific ammunition load for a caliber.
947
+ *
948
+ * @example
949
+ * ```ts
950
+ * const m855: Ammunition = {
951
+ * id: 'm855-62gr-fmj',
952
+ * caliberId: '5-56x45mm-nato',
953
+ * name: 'M855 62gr FMJ',
954
+ * bulletWeightG: 4.02,
955
+ * bulletType: 'FMJ',
956
+ * referenceVelocityMps: 940,
957
+ * referenceBarrelLengthMm: 508,
958
+ * isCommon: 1,
959
+ * createdAt: '2024-01-01T00:00:00',
960
+ * updatedAt: '2024-01-01T00:00:00',
961
+ * };
962
+ * ```
963
+ */
964
+ interface Ammunition {
965
+ /** URL-safe slug identifier. */
966
+ readonly id: string;
967
+ /** Caliber slug (FK to {@link Caliber.id}). */
968
+ caliberId: string;
969
+ /** Display name. */
970
+ name: string;
971
+ /** Military / NATO designation. */
972
+ designation?: string | null;
973
+ /** Ammunition manufacturer name. */
974
+ manufacturer?: string | null;
975
+ /** Country of origin ISO code. */
976
+ countryOfOrigin?: string | null;
977
+ /** Bullet weight in grams. */
978
+ bulletWeightG: number;
979
+ /** Bullet construction type (e.g. `"FMJ"`, `"JHP"`, `"AP"`). */
980
+ bulletType: string;
981
+ /** G1 ballistic coefficient (imperial). */
982
+ ballisticCoefficientG1?: number | null;
983
+ /** G7 ballistic coefficient (imperial). */
984
+ ballisticCoefficientG7?: number | null;
985
+ /** Reference muzzle velocity in m/s (measured from the reference barrel). */
986
+ referenceVelocityMps: number;
987
+ /** Barrel length in mm at which the reference velocity was measured. */
988
+ referenceBarrelLengthMm: number;
989
+ /** Exponent for velocity-vs-barrel-length power-law model. */
990
+ velocityRetentionExponent?: number | null;
991
+ /** Sectional density (lb/in^2). */
992
+ sectionalDensity?: number | null;
993
+ /** Long-form description. */
994
+ description?: string | null;
995
+ /** Year the load was introduced. */
996
+ yearIntroduced?: number | null;
997
+ /** Whether this is a common / widely-available load (`0` or `1`). */
998
+ isCommon: number;
999
+ /** Whether this is a military load (`0` or `1`). */
1000
+ isMilitary: number;
1001
+ /** ISO-8601 creation timestamp. */
1002
+ readonly createdAt: string;
1003
+ /** ISO-8601 last-update timestamp. */
1004
+ readonly updatedAt: string;
1005
+ }
1006
+ /**
1007
+ * A single point along a ballistic trajectory.
1008
+ *
1009
+ * Computed by the ammunition ballistics engine.
1010
+ */
1011
+ interface TrajectoryPoint {
1012
+ /** Distance from the muzzle in metres. */
1013
+ distanceM: number;
1014
+ /** Velocity at this distance in m/s. */
1015
+ velocityMps: number;
1016
+ /** Kinetic energy at this distance in joules. */
1017
+ energyJ: number;
1018
+ /** Bullet drop at this distance in centimetres. */
1019
+ dropCm: number;
1020
+ /** Time of flight to this distance in seconds. */
1021
+ timeOfFlightS: number;
1022
+ /** Mach number at this distance. */
1023
+ machNumber: number;
1024
+ /** Momentum at this distance in kg*m/s. */
1025
+ momentumKgMs: number;
1026
+ /** Taylor Knock-Out factor at this distance. */
1027
+ tkoFactor: number;
1028
+ /** Energy density at this distance in J/cm^2. */
1029
+ energyDensityJCm2: number;
1030
+ }
1031
+ /**
1032
+ * Muzzle-level terminal ballistics summary.
1033
+ */
1034
+ interface TerminalBallistics {
1035
+ /** Taylor Knock-Out factor. */
1036
+ tkoFactor: number;
1037
+ /** Momentum in kg*m/s. */
1038
+ momentumKgMs: number;
1039
+ /** Sectional density (lb/in^2). */
1040
+ sectionalDensity: number;
1041
+ /** Hatcher Relative Stopping Power. */
1042
+ hatcherRSP: number;
1043
+ /** Energy density in J/cm^2. */
1044
+ energyDensityJCm2: number;
1045
+ }
1046
+ /**
1047
+ * Full ballistic profile for an ammunition load.
1048
+ *
1049
+ * Returned by `GET /v1/ammunition/:id/ballistics`.
1050
+ */
1051
+ interface BallisticProfile {
1052
+ /** Ammunition summary. */
1053
+ ammunition: {
1054
+ id: string;
1055
+ name: string;
1056
+ caliberId: string;
1057
+ };
1058
+ /** Barrel length used for calculations in mm. */
1059
+ barrelLengthMm: number;
1060
+ /** Calculated muzzle velocity in m/s. */
1061
+ muzzleVelocityMps: number;
1062
+ /** Calculated muzzle energy in joules. */
1063
+ muzzleEnergyJ: number;
1064
+ /** Calculated effective range in metres. */
1065
+ effectiveRangeM: number;
1066
+ /** Terminal ballistics at the muzzle. */
1067
+ terminalBallistics: TerminalBallistics;
1068
+ /** Trajectory drop table at specified distances. */
1069
+ trajectory: TrajectoryPoint[];
1070
+ }
1071
+ /**
1072
+ * Firearm ballistic calculation result.
1073
+ *
1074
+ * Returned by `GET /v1/firearms/:id/calculate`.
1075
+ */
1076
+ interface FirearmCalculation {
1077
+ /** Firearm summary. */
1078
+ firearm: {
1079
+ id: string;
1080
+ name: string;
1081
+ barrelLengthMm?: number | null;
1082
+ };
1083
+ /** Ammunition summary (absent when barrel length is unavailable). */
1084
+ ammunition?: {
1085
+ id: string;
1086
+ name: string;
1087
+ };
1088
+ /** Calculated values. */
1089
+ calculated?: {
1090
+ muzzleVelocityMps: number;
1091
+ muzzleEnergyJ: number;
1092
+ terminalBallistics: TerminalBallistics;
1093
+ };
1094
+ /** Source-reported values from the firearm record. */
1095
+ sourceReported?: {
1096
+ muzzleVelocityMps: number | null;
1097
+ muzzleEnergyJ: number | null;
1098
+ };
1099
+ /** Delta between calculated and source-reported values. */
1100
+ delta?: {
1101
+ velocityMps: number | null;
1102
+ energyJ: number | null;
1103
+ };
1104
+ /** Error message when calculation cannot be performed. */
1105
+ error?: string;
1106
+ }
1107
+ /**
1108
+ * Firearm load profile including full trajectory.
1109
+ *
1110
+ * Returned by `GET /v1/firearms/:id/load`.
1111
+ */
1112
+ interface FirearmLoadProfile {
1113
+ /** Firearm summary. */
1114
+ firearm: {
1115
+ id: string;
1116
+ name: string;
1117
+ barrelLengthMm?: number | null;
1118
+ };
1119
+ /** Ammunition details (absent on error). */
1120
+ ammunition?: {
1121
+ id: string;
1122
+ name: string;
1123
+ caliberId: string;
1124
+ bulletWeightG: number;
1125
+ bulletType: string;
1126
+ };
1127
+ /** Calculated ballistic values. */
1128
+ calculated?: {
1129
+ muzzleVelocityMps: number;
1130
+ muzzleEnergyJ: number;
1131
+ effectiveRangeM: number;
1132
+ terminalBallistics: TerminalBallistics;
1133
+ };
1134
+ /** Source-reported values from the firearm record. */
1135
+ sourceReported?: {
1136
+ muzzleVelocityMps: number | null;
1137
+ muzzleEnergyJ: number | null;
1138
+ effectiveRangeM: number | null;
1139
+ };
1140
+ /** Trajectory drop table. */
1141
+ trajectory?: TrajectoryPoint[];
1142
+ /** Error message when calculation cannot be performed. */
1143
+ error?: string;
1144
+ }
1145
+ /**
1146
+ * A firearm category (e.g. "semi-automatic-pistol", "bolt-action-rifle").
1147
+ */
1148
+ interface Category {
1149
+ /** URL-safe slug identifier. */
1150
+ readonly id: string;
1151
+ /** Display name. */
1152
+ name: string;
1153
+ /** Long-form description. */
1154
+ description?: string | null;
1155
+ }
1156
+ /**
1157
+ * An image associated with a firearm.
1158
+ */
1159
+ interface FirearmImage {
1160
+ /** Auto-increment integer ID. */
1161
+ readonly id: number;
1162
+ /** Firearm slug (FK to {@link Firearm.id}). */
1163
+ firearmId: string;
1164
+ /** Image URL. */
1165
+ url: string;
1166
+ /** Image type (e.g. `"gallery"`). */
1167
+ type?: string | null;
1168
+ /** Image source attribution. */
1169
+ source?: string | null;
1170
+ /** Image license. */
1171
+ license?: string | null;
1172
+ }
1173
+ /**
1174
+ * A military, law-enforcement, or other institutional user of a firearm.
1175
+ */
1176
+ interface FirearmUser {
1177
+ /** Auto-increment integer ID. */
1178
+ readonly id: number;
1179
+ /** Firearm slug (FK to {@link Firearm.id}). */
1180
+ firearmId: string;
1181
+ /** User / organisation name (e.g. `"U.S. Army"`). */
1182
+ userName: string;
1183
+ /** User type (e.g. `"military"`, `"law_enforcement"`). */
1184
+ userType?: string | null;
1185
+ /** ISO 3166-1 alpha-2 country code. */
1186
+ countryCode?: string | null;
1187
+ /** Year the firearm was adopted. */
1188
+ adoptedYear?: number | null;
1189
+ /** Service designation (e.g. `"M9"`, `"P226"`). */
1190
+ designation?: string | null;
1191
+ }
1192
+ /**
1193
+ * Extracted game statistics for a firearm (0-100 scale per stat).
1194
+ *
1195
+ * Used in game-related endpoints.
1196
+ */
1197
+ interface GameStats {
1198
+ /** Damage rating (0-100). */
1199
+ damage: number | null;
1200
+ /** Accuracy rating (0-100). */
1201
+ accuracy: number | null;
1202
+ /** Range rating (0-100). */
1203
+ range: number | null;
1204
+ /** Fire rate rating (0-100). */
1205
+ fireRate: number | null;
1206
+ /** Mobility rating (0-100). */
1207
+ mobility: number | null;
1208
+ /** Recoil control rating (0-100). */
1209
+ recoilControl: number | null;
1210
+ /** Reload speed rating (0-100). */
1211
+ reloadSpeed: number | null;
1212
+ /** Concealment rating (0-100). */
1213
+ concealment: number | null;
1214
+ }
1215
+ /**
1216
+ * Firearm game profile including archetype classification.
1217
+ *
1218
+ * Returned by `GET /v1/firearms/:id/game/profile`.
1219
+ */
1220
+ interface GameProfile {
1221
+ /** Firearm slug. */
1222
+ id: string;
1223
+ /** Firearm name. */
1224
+ name: string;
1225
+ /** Damage rating (0-100). */
1226
+ gameDamage: number | null;
1227
+ /** Accuracy rating (0-100). */
1228
+ gameAccuracy: number | null;
1229
+ /** Range rating (0-100). */
1230
+ gameRange: number | null;
1231
+ /** Fire rate rating (0-100). */
1232
+ gameFireRate: number | null;
1233
+ /** Mobility rating (0-100). */
1234
+ gameMobility: number | null;
1235
+ /** Recoil control rating (0-100). */
1236
+ gameRecoilControl: number | null;
1237
+ /** Reload speed rating (0-100). */
1238
+ gameReloadSpeed: number | null;
1239
+ /** Concealment rating (0-100). */
1240
+ gameConcealment: number | null;
1241
+ /** Computed archetype classification. */
1242
+ archetype: string;
1243
+ /** List of stat-based strengths. */
1244
+ strengths: string[];
1245
+ /** List of stat-based weaknesses. */
1246
+ weaknesses: string[];
1247
+ }
1248
+ /**
1249
+ * A firearm in the game meta listing.
1250
+ *
1251
+ * Returned by `GET /v1/firearms/:id/game/meta`.
1252
+ */
1253
+ interface GameMetaItem {
1254
+ /** Firearm slug. */
1255
+ id: string;
1256
+ /** Firearm name. */
1257
+ name: string;
1258
+ /** Computed archetype. */
1259
+ archetype: string;
1260
+ gameDamage: number | null;
1261
+ gameAccuracy: number | null;
1262
+ gameRange: number | null;
1263
+ gameFireRate: number | null;
1264
+ gameMobility: number | null;
1265
+ gameRecoilControl: number | null;
1266
+ gameReloadSpeed: number | null;
1267
+ gameConcealment: number | null;
1268
+ }
1269
+ /**
1270
+ * Balance report entry flagging statistical outliers.
1271
+ *
1272
+ * Returned by `GET /v1/game/balance`.
1273
+ */
1274
+ interface BalanceEntry {
1275
+ /** Firearm slug. */
1276
+ firearmId: string;
1277
+ /** Firearm name. */
1278
+ firearmName: string;
1279
+ /** Stat deviations that exceeded the threshold. */
1280
+ deviations: BalanceDeviation[];
1281
+ }
1282
+ /**
1283
+ * A single stat deviation in a balance report.
1284
+ */
1285
+ interface BalanceDeviation {
1286
+ /** Stat key (e.g. `"damage"`, `"accuracy"`). */
1287
+ stat: string;
1288
+ /** The firearm's value for this stat. */
1289
+ value: number;
1290
+ /** Population mean for this stat. */
1291
+ mean: number;
1292
+ /** Population standard deviation for this stat. */
1293
+ stdDev: number;
1294
+ /** Z-score (how many std devs from the mean). */
1295
+ zScore: number;
1296
+ }
1297
+ /**
1298
+ * Tier list grouping for a single game stat.
1299
+ *
1300
+ * Returned by `GET /v1/game/tier-list`.
1301
+ */
1302
+ interface TierList {
1303
+ /** The stat used for ranking. */
1304
+ stat: string;
1305
+ /** Firearms grouped into S/A/B/C/D tiers. */
1306
+ tiers: {
1307
+ S: TierItem[];
1308
+ A: TierItem[];
1309
+ B: TierItem[];
1310
+ C: TierItem[];
1311
+ D: TierItem[];
1312
+ };
1313
+ }
1314
+ /**
1315
+ * An item within a tier.
1316
+ */
1317
+ interface TierItem {
1318
+ /** Firearm slug. */
1319
+ id: string;
1320
+ /** Firearm name. */
1321
+ name: string;
1322
+ /** Category slug. */
1323
+ categoryId: string | null;
1324
+ /** Stat value. */
1325
+ value: number;
1326
+ }
1327
+ /**
1328
+ * Game matchup result comparing two firearms' game stats.
1329
+ *
1330
+ * Returned by `GET /v1/game/matchups`.
1331
+ */
1332
+ interface MatchupResult {
1333
+ /** Firearm A summary with stats. */
1334
+ a: {
1335
+ id: string;
1336
+ name: string;
1337
+ stats: GameStats;
1338
+ };
1339
+ /** Firearm B summary with stats. */
1340
+ b: {
1341
+ id: string;
1342
+ name: string;
1343
+ stats: GameStats;
1344
+ };
1345
+ /** Per-stat verdict: which firearm wins each category. */
1346
+ verdicts: Record<string, 'a' | 'b' | 'draw'>;
1347
+ /** Number of stats won by A. */
1348
+ aWins: number;
1349
+ /** Number of stats won by B. */
1350
+ bWins: number;
1351
+ /** Number of drawn stats. */
1352
+ draws: number;
1353
+ }
1354
+ /**
1355
+ * A firearm in a role roster.
1356
+ *
1357
+ * Returned by `GET /v1/game/role-roster`.
1358
+ */
1359
+ interface RoleRosterItem {
1360
+ /** Firearm slug. */
1361
+ id: string;
1362
+ /** Firearm name. */
1363
+ name: string;
1364
+ /** Weighted score for the requested role. */
1365
+ roleScore: number;
1366
+ /** Full game stats. */
1367
+ stats: GameStats;
1368
+ }
1369
+ /**
1370
+ * Stat distribution / histogram for a single game stat.
1371
+ *
1372
+ * Returned by `GET /v1/game/stat-distribution`.
1373
+ */
1374
+ interface StatDistribution {
1375
+ /** The stat analysed. */
1376
+ stat: string;
1377
+ /** Number of firearms with this stat. */
1378
+ count: number;
1379
+ /** Arithmetic mean. */
1380
+ mean: number;
1381
+ /** Median value. */
1382
+ median: number;
1383
+ /** Standard deviation. */
1384
+ stdDev: number;
1385
+ /** Minimum value. */
1386
+ min: number;
1387
+ /** Maximum value. */
1388
+ max: number;
1389
+ /** Key percentiles. */
1390
+ percentiles: {
1391
+ p10: number;
1392
+ p25: number;
1393
+ p50: number;
1394
+ p75: number;
1395
+ p90: number;
1396
+ };
1397
+ /** 10-bucket histogram (0-10, 10-20, ..., 90-100). */
1398
+ histogram: Array<{
1399
+ bucket: string;
1400
+ count: number;
1401
+ }>;
1402
+ }
1403
+ /**
1404
+ * A versioned snapshot of game stats for all firearms.
1405
+ *
1406
+ * Returned by `GET /v1/game-stats/versions`.
1407
+ */
1408
+ interface GameStatsVersion {
1409
+ /** Auto-increment ID. */
1410
+ readonly id: number;
1411
+ /** Version identifier (e.g. `"1.0.0"`). */
1412
+ version: string;
1413
+ /** Description of the snapshot. */
1414
+ description?: string | null;
1415
+ /** Number of firearms in this snapshot. */
1416
+ firearmCount: number;
1417
+ /** ISO-8601 creation timestamp. */
1418
+ readonly createdAt: string;
1419
+ }
1420
+ /**
1421
+ * A single entry in a game-stats snapshot.
1422
+ */
1423
+ interface GameStatsSnapshotEntry {
1424
+ /** Auto-increment ID. */
1425
+ readonly id: number;
1426
+ /** Snapshot ID. */
1427
+ snapshotId: number;
1428
+ /** Firearm slug. */
1429
+ firearmId: string;
1430
+ /** Firearm name at time of snapshot. */
1431
+ firearmName: string;
1432
+ gameDamage: number | null;
1433
+ gameAccuracy: number | null;
1434
+ gameRange: number | null;
1435
+ gameFireRate: number | null;
1436
+ gameMobility: number | null;
1437
+ gameRecoilControl: number | null;
1438
+ gameReloadSpeed: number | null;
1439
+ gameConcealment: number | null;
1440
+ }
1441
+ /**
1442
+ * Comparison result for multiple firearms.
1443
+ *
1444
+ * Returned by `GET /v1/firearms/compare`.
1445
+ */
1446
+ interface FirearmComparison {
1447
+ /** Full detail records for each compared firearm. */
1448
+ items: FirearmDetail[];
1449
+ /** Per-field deltas between the compared firearms. */
1450
+ deltas: Record<string, unknown>;
1451
+ }
1452
+ /**
1453
+ * Head-to-head comparison of two firearms on numeric specs.
1454
+ *
1455
+ * Returned by `GET /v1/firearms/head-to-head`.
1456
+ */
1457
+ interface HeadToHead {
1458
+ /** Firearm A raw data (snake_case keys from D1). */
1459
+ a: Record<string, unknown>;
1460
+ /** Firearm B raw data (snake_case keys from D1). */
1461
+ b: Record<string, unknown>;
1462
+ /** Per-field verdicts indicating which firearm is superior. */
1463
+ verdicts: Record<string, {
1464
+ /** Which firearm wins for this field. */
1465
+ winner: 'a' | 'b' | 'draw';
1466
+ /** Value for firearm A. */
1467
+ a: number | null;
1468
+ /** Value for firearm B. */
1469
+ b: number | null;
1470
+ /** Human-readable description of why the winner is better. */
1471
+ better: string;
1472
+ }>;
1473
+ }
1474
+ /**
1475
+ * Firearm family tree (ancestors + current + descendants).
1476
+ *
1477
+ * Returned by `GET /v1/firearms/:id/family`.
1478
+ */
1479
+ interface FamilyTree {
1480
+ /** Ancestor firearms (oldest first). */
1481
+ ancestors: Record<string, unknown>[];
1482
+ /** The requested firearm (full record). */
1483
+ current: Firearm;
1484
+ /** Descendant / variant firearms. */
1485
+ descendants: Record<string, unknown>[];
1486
+ }
1487
+ /**
1488
+ * A similar firearm with a computed similarity score.
1489
+ *
1490
+ * Returned by `GET /v1/firearms/:id/similar`.
1491
+ */
1492
+ interface SimilarFirearm {
1493
+ /** Firearm slug. */
1494
+ id: string;
1495
+ /** Firearm name. */
1496
+ name: string;
1497
+ /** Similarity score (higher is more similar). */
1498
+ score: number;
1499
+ }
1500
+ /**
1501
+ * Adoption map for a firearm, grouped by country.
1502
+ *
1503
+ * Returned by `GET /v1/firearms/:id/adoption`.
1504
+ */
1505
+ interface AdoptionMap {
1506
+ /** Firearm slug. */
1507
+ firearmId: string;
1508
+ /** Firearm name. */
1509
+ firearmName: string;
1510
+ /** Users grouped by country. */
1511
+ countries: Array<{
1512
+ /** ISO country code (may be `null` for unknown). */
1513
+ code: string | null;
1514
+ /** Institutional users in this country. */
1515
+ users: Array<{
1516
+ name: string;
1517
+ type: string | null;
1518
+ year: number | null;
1519
+ designation: string | null;
1520
+ }>;
1521
+ }>;
1522
+ }
1523
+ /**
1524
+ * A firearm's computed power rating with breakdown.
1525
+ *
1526
+ * Returned by `GET /v1/firearms/power-rating`.
1527
+ */
1528
+ interface PowerRating {
1529
+ /** Firearm slug. */
1530
+ id: string;
1531
+ /** Firearm name. */
1532
+ name: string;
1533
+ /** Manufacturer slug. */
1534
+ manufacturerId: string;
1535
+ /** Category slug. */
1536
+ categoryId: string;
1537
+ /** Composite power rating (0-100 scale). */
1538
+ powerRating: number;
1539
+ /** Per-component breakdown of the power rating. */
1540
+ breakdown: {
1541
+ /** Energy component (max 30). */
1542
+ energy: number;
1543
+ /** Range component (max 25). */
1544
+ range: number;
1545
+ /** Fire rate component (max 20). */
1546
+ fireRate: number;
1547
+ /** Capacity component (max 15). */
1548
+ capacity: number;
1549
+ /** Mobility component (max 10). */
1550
+ mobility: number;
1551
+ };
1552
+ }
1553
+ /**
1554
+ * Firearm dimensions in both metric and imperial units.
1555
+ *
1556
+ * Returned by `GET /v1/firearms/:id/dimensions`.
1557
+ */
1558
+ interface Dimensions {
1559
+ /** Firearm slug. */
1560
+ id: string;
1561
+ /** Firearm name. */
1562
+ name: string;
1563
+ /** Metric measurements. */
1564
+ metric: {
1565
+ weightEmptyG: number | null;
1566
+ weightLoadedG: number | null;
1567
+ overallLengthMm: number | null;
1568
+ barrelLengthMm: number | null;
1569
+ heightMm: number | null;
1570
+ widthMm: number | null;
1571
+ foldedLengthMm: number | null;
1572
+ };
1573
+ /** Imperial measurements (converted from metric). */
1574
+ imperial: {
1575
+ weightEmptyLbs: number | null;
1576
+ weightLoadedLbs: number | null;
1577
+ overallLengthIn: number | null;
1578
+ barrelLengthIn: number | null;
1579
+ heightIn: number | null;
1580
+ widthIn: number | null;
1581
+ foldedLengthIn: number | null;
1582
+ };
1583
+ }
1584
+ /**
1585
+ * Available filter dropdown options.
1586
+ *
1587
+ * Returned by `GET /v1/firearms/filters`.
1588
+ */
1589
+ interface FilterOptions {
1590
+ /** Available categories. */
1591
+ categories: Array<{
1592
+ slug: string;
1593
+ name: string;
1594
+ }>;
1595
+ /** Available manufacturers. */
1596
+ manufacturers: Array<{
1597
+ id: string;
1598
+ name: string;
1599
+ }>;
1600
+ /** Available calibers. */
1601
+ calibers: Array<{
1602
+ id: string;
1603
+ name: string;
1604
+ }>;
1605
+ /** Available action types. */
1606
+ actionTypes: Array<{
1607
+ id: string;
1608
+ name: string;
1609
+ }>;
1610
+ }
1611
+ /**
1612
+ * Database summary statistics.
1613
+ *
1614
+ * Returned by `GET /v1/stats/summary`.
1615
+ */
1616
+ interface StatsSummary {
1617
+ /** Total number of firearms. */
1618
+ total_firearms: number;
1619
+ /** Total number of manufacturers. */
1620
+ total_manufacturers: number;
1621
+ /** Total number of calibers. */
1622
+ total_calibers: number;
1623
+ /** Number of distinct countries of origin. */
1624
+ countries_of_origin: number;
1625
+ /** Average data confidence percentage. */
1626
+ avg_confidence: number | null;
1627
+ /** Total number of firearm variants. */
1628
+ total_variants: number;
1629
+ }
1630
+ /**
1631
+ * Production status breakdown.
1632
+ *
1633
+ * Returned by `GET /v1/stats/production-status`.
1634
+ */
1635
+ interface ProductionStatusItem {
1636
+ /** Status value. */
1637
+ status: string;
1638
+ /** Number of firearms with this status. */
1639
+ count: number;
1640
+ }
1641
+ /**
1642
+ * Field coverage percentage for a single field.
1643
+ *
1644
+ * Returned by `GET /v1/stats/field-coverage`.
1645
+ */
1646
+ interface FieldCoverage {
1647
+ /** Field name (snake_case column name). */
1648
+ field: string;
1649
+ /** Percentage of firearms with a non-null value (0-100). */
1650
+ percentage: number;
1651
+ }
1652
+ /**
1653
+ * Popular caliber with firearm count.
1654
+ *
1655
+ * Returned by `GET /v1/stats/popular-calibers`.
1656
+ */
1657
+ interface PopularCaliber {
1658
+ /** Caliber slug. */
1659
+ id: string;
1660
+ /** Caliber display name. */
1661
+ name: string;
1662
+ /** NATO designation. */
1663
+ nato_designation: string | null;
1664
+ /** Number of firearms chambered in this caliber. */
1665
+ firearm_count: number;
1666
+ }
1667
+ /**
1668
+ * Prolific manufacturer with firearm count.
1669
+ *
1670
+ * Returned by `GET /v1/stats/prolific-manufacturers`.
1671
+ */
1672
+ interface ProlificManufacturer {
1673
+ /** Manufacturer slug. */
1674
+ id: string;
1675
+ /** Manufacturer name. */
1676
+ name: string;
1677
+ /** Country code. */
1678
+ country_code: string | null;
1679
+ /** Number of firearms produced. */
1680
+ firearm_count: number;
1681
+ }
1682
+ /**
1683
+ * Category statistics with averages.
1684
+ *
1685
+ * Returned by `GET /v1/stats/by-category`.
1686
+ */
1687
+ interface CategoryStats {
1688
+ /** Category slug. */
1689
+ id: string;
1690
+ /** Category name. */
1691
+ name: string;
1692
+ /** Number of firearms in this category. */
1693
+ firearm_count: number;
1694
+ /** Average weight in grams. */
1695
+ avg_weight_g: number | null;
1696
+ /** Average magazine capacity. */
1697
+ avg_magazine_capacity: number | null;
1698
+ /** Average barrel length in mm. */
1699
+ avg_barrel_length_mm: number | null;
1700
+ }
1701
+ /**
1702
+ * Era (decade) statistics.
1703
+ *
1704
+ * Returned by `GET /v1/stats/by-era`.
1705
+ */
1706
+ interface EraStats {
1707
+ /** Number of firearms introduced in this decade. */
1708
+ firearm_count: number;
1709
+ /** Average weight in grams. */
1710
+ avg_weight_g: number | null;
1711
+ /** Average magazine capacity. */
1712
+ avg_magazine_capacity: number | null;
1713
+ /** Average barrel length in mm. */
1714
+ avg_barrel_length_mm: number | null;
1715
+ /** Earliest year in the decade with data. */
1716
+ earliest_year: number | null;
1717
+ /** Latest year in the decade with data. */
1718
+ latest_year: number | null;
1719
+ }
1720
+ /**
1721
+ * Material usage breakdown per component.
1722
+ *
1723
+ * Returned by `GET /v1/stats/materials`.
1724
+ */
1725
+ interface MaterialStats {
1726
+ /** Frame materials. */
1727
+ frame: Array<{
1728
+ material: string;
1729
+ count: number;
1730
+ }>;
1731
+ /** Slide materials. */
1732
+ slide: Array<{
1733
+ material: string;
1734
+ count: number;
1735
+ }>;
1736
+ /** Barrel materials. */
1737
+ barrel: Array<{
1738
+ material: string;
1739
+ count: number;
1740
+ }>;
1741
+ /** Stock materials. */
1742
+ stock: Array<{
1743
+ material: string;
1744
+ count: number;
1745
+ }>;
1746
+ }
1747
+ /**
1748
+ * Adoption record by country.
1749
+ *
1750
+ * Returned by `GET /v1/stats/adoption/country`.
1751
+ */
1752
+ interface AdoptionByCountryItem {
1753
+ /** Firearm user record ID. */
1754
+ id: number;
1755
+ /** User / organisation name. */
1756
+ user_name: string;
1757
+ /** User type. */
1758
+ user_type: string | null;
1759
+ /** Year adopted. */
1760
+ adopted_year: number | null;
1761
+ /** Service designation. */
1762
+ designation: string | null;
1763
+ /** Firearm slug. */
1764
+ firearm_id: string;
1765
+ /** Firearm name. */
1766
+ firearm_name: string;
1767
+ }
1768
+ /**
1769
+ * Adoption record by user type.
1770
+ *
1771
+ * Returned by `GET /v1/stats/adoption/type`.
1772
+ */
1773
+ interface AdoptionByTypeItem {
1774
+ /** Firearm slug. */
1775
+ firearm_id: string;
1776
+ /** Firearm name. */
1777
+ firearm_name: string;
1778
+ /** Number of countries that adopted this firearm for this role. */
1779
+ adoption_count: number;
1780
+ /** List of country codes. */
1781
+ countries: string[];
1782
+ }
1783
+ /**
1784
+ * Action type frequency.
1785
+ *
1786
+ * Returned by `GET /v1/stats/action-types`.
1787
+ */
1788
+ interface ActionTypeStats {
1789
+ /** Action type slug. */
1790
+ action_type: string;
1791
+ /** Number of firearms with this action type. */
1792
+ count: number;
1793
+ }
1794
+ /**
1795
+ * Feature frequency item.
1796
+ *
1797
+ * Returned by `GET /v1/stats/feature-frequency`.
1798
+ */
1799
+ interface FeatureFrequency {
1800
+ /** Feature name. */
1801
+ feature: string;
1802
+ /** Number of firearms with this feature. */
1803
+ count: number;
1804
+ }
1805
+ /**
1806
+ * Caliber popularity within a decade.
1807
+ *
1808
+ * Returned by `GET /v1/stats/caliber-popularity-by-era`.
1809
+ */
1810
+ interface CaliberPopularityByEra {
1811
+ /** Decade label (e.g. `"1990s"`). */
1812
+ decade: string;
1813
+ /** Calibers ranked by popularity within this decade. */
1814
+ calibers: Array<{
1815
+ caliberId: string;
1816
+ caliberName: string;
1817
+ firearmCount: number;
1818
+ }>;
1819
+ }
1820
+ /**
1821
+ * A country with its firearm count.
1822
+ *
1823
+ * Returned by `GET /v1/countries`.
1824
+ */
1825
+ interface Country {
1826
+ /** ISO 3166-1 alpha-2 country code. */
1827
+ code: string;
1828
+ /** Number of firearms originating from this country. */
1829
+ firearmCount: number;
1830
+ }
1831
+ /**
1832
+ * A conflict with associated firearms.
1833
+ *
1834
+ * Returned by `GET /v1/conflicts`.
1835
+ */
1836
+ interface Conflict {
1837
+ /** Conflict name. */
1838
+ name: string;
1839
+ /** Number of firearms used in this conflict. */
1840
+ firearmCount: number;
1841
+ /** Firearms used in this conflict. */
1842
+ firearms: Array<{
1843
+ id: string;
1844
+ name: string;
1845
+ }>;
1846
+ }
1847
+ /**
1848
+ * Data coverage summary.
1849
+ *
1850
+ * Returned by `GET /v1/data/coverage`.
1851
+ */
1852
+ interface DataCoverage {
1853
+ /** Field name. */
1854
+ field: string;
1855
+ /** Percentage of records with non-null values (0-100). */
1856
+ percentage: number;
1857
+ }
1858
+ /**
1859
+ * A firearm record with its computed data confidence score.
1860
+ *
1861
+ * Returned by the data quality confidence endpoint.
1862
+ */
1863
+ interface ConfidenceEntry {
1864
+ /** Firearm slug. */
1865
+ slug: string;
1866
+ /** Firearm display name. */
1867
+ name: string;
1868
+ /** Confidence score (0-1). */
1869
+ confidenceScore: number;
1870
+ /** Number of non-null specification fields. */
1871
+ filledFields: number;
1872
+ /** Total number of specification fields. */
1873
+ totalFields: number;
1874
+ }
1875
+ /**
1876
+ * A country's firearm arsenal — military and law enforcement holdings.
1877
+ */
1878
+ interface CountryArsenal {
1879
+ /** The country record. */
1880
+ country: Country;
1881
+ /** Total number of adopted firearms. */
1882
+ totalFirearms: number;
1883
+ /** Firearms grouped by usage type. */
1884
+ groups: {
1885
+ /** Usage type (e.g. "military", "law_enforcement"). */
1886
+ type: string;
1887
+ /** Firearms in this usage group. */
1888
+ firearms: Firearm[];
1889
+ }[];
1890
+ }
1891
+ /**
1892
+ * Ballistics data for a caliber at a given distance.
1893
+ */
1894
+ interface BallisticsResult {
1895
+ /** Caliber slug or ID. */
1896
+ caliberId: string;
1897
+ /** Distance in meters. */
1898
+ distanceM: number;
1899
+ /** Bullet velocity at the given distance (fps). */
1900
+ velocityFps: number;
1901
+ /** Bullet energy at the given distance (ft-lbs). */
1902
+ energyFtLbs: number;
1903
+ /** Bullet drop at the given distance (inches). */
1904
+ dropInches: number;
1905
+ /** Wind drift at the given distance (inches). */
1906
+ driftInches?: number;
1907
+ /** Time of flight in seconds. */
1908
+ timeOfFlightS?: number;
1909
+ }
1910
+ /**
1911
+ * SVG silhouette data for a firearm.
1912
+ */
1913
+ interface Silhouette {
1914
+ /** The firearm slug. */
1915
+ slug: string;
1916
+ /** Raw SVG string (when format is "svg"). */
1917
+ svg?: string;
1918
+ /** Base64 data URI (when format is "datauri"). */
1919
+ dataUri?: string;
1920
+ /** Stroke width used. */
1921
+ strokeWidth?: number;
1922
+ /** Stroke color used. */
1923
+ strokeColor?: string;
1924
+ /** Original image width in pixels. */
1925
+ width?: number;
1926
+ /** Original image height in pixels. */
1927
+ height?: number;
1928
+ }
1929
+ /**
1930
+ * Game balance report identifying outlier firearms.
1931
+ */
1932
+ interface BalanceReport {
1933
+ /** Threshold percentage used for outlier detection. */
1934
+ threshold: number;
1935
+ /** Total firearms analyzed. */
1936
+ totalAnalyzed: number;
1937
+ /** Firearms flagged as overpowered. */
1938
+ overpowered: BalanceEntry[];
1939
+ /** Firearms flagged as underpowered. */
1940
+ underpowered: BalanceEntry[];
1941
+ /** Deviations by stat. */
1942
+ deviations?: BalanceDeviation[];
1943
+ }
1944
+ /**
1945
+ * A roster of firearms suited for a specific game role.
1946
+ */
1947
+ interface RoleRoster {
1948
+ /** The role that was queried. */
1949
+ role: string;
1950
+ /** Total firearms considered. */
1951
+ totalConsidered: number;
1952
+ /** Firearms ranked by suitability for the role. */
1953
+ firearms: RoleRosterItem[];
1954
+ }
1955
+ /**
1956
+ * A user's favorited firearm.
1957
+ *
1958
+ * Returned by `GET /v1/me/favorites`.
1959
+ */
1960
+ interface Favorite {
1961
+ /** Firearm slug. */
1962
+ firearmId: string;
1963
+ /** ISO-8601 timestamp when the favorite was added. */
1964
+ createdAt: string;
1965
+ }
1966
+ /**
1967
+ * Result of toggling a favorite.
1968
+ *
1969
+ * Returned by `POST /v1/me/favorites/:firearmId` and `DELETE /v1/me/favorites/:firearmId`.
1970
+ */
1971
+ interface FavoriteToggle {
1972
+ /** Firearm slug. */
1973
+ firearmId: string;
1974
+ /** Whether the firearm is now favorited. */
1975
+ favorited: boolean;
1976
+ }
1977
+ /**
1978
+ * A user-submitted data quality report.
1979
+ *
1980
+ * Returned by `GET /v1/me/reports` and `POST /v1/me/reports`.
1981
+ */
1982
+ interface DataReport {
1983
+ /** Unique report ID. */
1984
+ readonly id: string;
1985
+ /** Firearm slug the report pertains to. */
1986
+ firearmId: string;
1987
+ /** Section of the firearm page (e.g. `"specs"`, `"game-stats"`). */
1988
+ section: string;
1989
+ /** Type of issue reported. */
1990
+ issueType: 'incorrect' | 'missing' | 'outdated';
1991
+ /** User-provided description of the issue. */
1992
+ description: string;
1993
+ /** Optional reference URLs supporting the report. */
1994
+ references?: string[];
1995
+ /** Optional suggested correction. */
1996
+ suggestedValue?: string;
1997
+ /** Current review status. */
1998
+ status: 'pending' | 'reviewed' | 'resolved' | 'dismissed';
1999
+ /** ISO-8601 creation timestamp. */
2000
+ readonly createdAt: string;
2001
+ /** ISO-8601 last-update timestamp. */
2002
+ readonly updatedAt: string;
2003
+ }
2004
+ /**
2005
+ * A support ticket summary.
2006
+ *
2007
+ * Returned by `GET /v1/me/support`.
2008
+ */
2009
+ interface SupportTicket {
2010
+ /** Unique ticket ID. */
2011
+ readonly id: string;
2012
+ /** Ticket subject line. */
2013
+ subject: string;
2014
+ /** Ticket description / body. */
2015
+ description: string;
2016
+ /** Ticket category. */
2017
+ category?: 'billing' | 'technical' | 'data_quality' | 'feature_request' | 'other';
2018
+ /** Priority level. */
2019
+ priority: 'low' | 'normal' | 'high' | 'urgent';
2020
+ /** Current status. */
2021
+ status: 'open' | 'in_progress' | 'waiting' | 'resolved' | 'closed';
2022
+ /** ISO-8601 creation timestamp. */
2023
+ readonly createdAt: string;
2024
+ /** ISO-8601 last-update timestamp. */
2025
+ readonly updatedAt: string;
2026
+ }
2027
+ /**
2028
+ * A support ticket with its reply thread.
2029
+ *
2030
+ * Returned by `GET /v1/me/support/:ticketId`.
2031
+ */
2032
+ interface SupportTicketDetail extends SupportTicket {
2033
+ /** Reply thread ordered chronologically. */
2034
+ replies: SupportTicketReply[];
2035
+ }
2036
+ /**
2037
+ * A single reply in a support ticket thread.
2038
+ */
2039
+ interface SupportTicketReply {
2040
+ /** Auto-increment reply ID. */
2041
+ readonly id: number;
2042
+ /** Parent ticket ID. */
2043
+ ticketId: string;
2044
+ /** Reply message body. */
2045
+ message: string;
2046
+ /** Whether this reply was from staff. */
2047
+ isStaff: boolean;
2048
+ /** ISO-8601 creation timestamp. */
2049
+ readonly createdAt: string;
2050
+ }
2051
+ /**
2052
+ * A webhook endpoint configuration.
2053
+ *
2054
+ * Returned by `GET /v1/me/webhooks` and related endpoints.
2055
+ */
2056
+ interface WebhookEndpoint {
2057
+ /** Unique endpoint ID. */
2058
+ readonly id: string;
2059
+ /** Delivery URL. */
2060
+ url: string;
2061
+ /** Optional human-readable description. */
2062
+ description?: string | null;
2063
+ /** Event types this endpoint subscribes to. */
2064
+ events: string[];
2065
+ /** Whether the endpoint is active. */
2066
+ active: boolean;
2067
+ /** HMAC signing secret for verifying payloads. */
2068
+ secret: string;
2069
+ /** ISO-8601 creation timestamp. */
2070
+ readonly createdAt: string;
2071
+ /** ISO-8601 last-update timestamp. */
2072
+ readonly updatedAt: string;
2073
+ }
2074
+ /**
2075
+ * Result of sending a test event to a webhook endpoint.
2076
+ *
2077
+ * Returned by `POST /v1/me/webhooks/:id/test`.
2078
+ */
2079
+ interface WebhookTestResult {
2080
+ /** Whether the test delivery was successful. */
2081
+ success: boolean;
2082
+ /** HTTP status code returned by the endpoint. */
2083
+ statusCode: number;
2084
+ /** Round-trip delivery time in milliseconds. */
2085
+ responseTimeMs: number;
2086
+ /** Error message if delivery failed. */
2087
+ error?: string;
2088
+ }
2089
+ /**
2090
+ * API usage statistics for the authenticated user.
2091
+ *
2092
+ * Returned by `GET /v1/me/usage`.
2093
+ */
2094
+ interface UsageStats {
2095
+ /** Current billing month usage. */
2096
+ currentMonth: {
2097
+ /** Requests used this month. */
2098
+ used: number;
2099
+ /** Monthly request limit. */
2100
+ limit: number;
2101
+ /** Usage as a percentage (0-100). */
2102
+ percentage: number;
2103
+ /** ISO-8601 timestamp when usage resets. */
2104
+ resetsAt: string;
2105
+ };
2106
+ /** Daily request counts for the requested period. */
2107
+ dailyBreakdown: Array<{
2108
+ date: string;
2109
+ count: number;
2110
+ }>;
2111
+ /** Per-API-key usage breakdown. */
2112
+ perKey: Array<{
2113
+ keyId: string;
2114
+ keyName: string;
2115
+ count: number;
2116
+ }>;
2117
+ /** Total number of API keys. */
2118
+ keyCount: number;
2119
+ /** Current subscription tier details. */
2120
+ tier: {
2121
+ /** Tier display name. */
2122
+ name: string;
2123
+ /** Monthly request allowance. */
2124
+ requestsPerMonth: number;
2125
+ /** Requests per minute limit. */
2126
+ rateLimit: number;
2127
+ };
2128
+ }
2129
+
2130
+ /**
2131
+ * Common pagination parameters accepted by all list endpoints.
2132
+ *
2133
+ * @example
2134
+ * ```ts
2135
+ * const params: PaginationParams = { page: 2, per_page: 50, order: 'desc' };
2136
+ * ```
2137
+ */
2138
+ interface PaginationParams {
2139
+ /**
2140
+ * Page number (1-based).
2141
+ * @defaultValue `1`
2142
+ */
2143
+ page?: number;
2144
+ /**
2145
+ * Number of items per page (1-100).
2146
+ * @defaultValue `20`
2147
+ */
2148
+ per_page?: number;
2149
+ /** Column to sort by. Allowed values depend on the endpoint. */
2150
+ sort?: string;
2151
+ /**
2152
+ * Sort direction.
2153
+ * @defaultValue `"asc"`
2154
+ */
2155
+ order?: 'asc' | 'desc';
2156
+ }
2157
+ /**
2158
+ * Parameters for `GET /v1/firearms`.
2159
+ *
2160
+ * @example
2161
+ * ```ts
2162
+ * const params: ListFirearmsParams = {
2163
+ * manufacturer: 'glock',
2164
+ * category: 'semi-automatic-pistol',
2165
+ * sort: 'name',
2166
+ * page: 1,
2167
+ * per_page: 20,
2168
+ * };
2169
+ * ```
2170
+ */
2171
+ interface ListFirearmsParams extends PaginationParams {
2172
+ /** Filter by manufacturer slug. */
2173
+ manufacturer?: string;
2174
+ /** Filter by caliber slug (matched via junction table). */
2175
+ caliber?: string;
2176
+ /** Filter by category slug. */
2177
+ category?: string;
2178
+ /** Filter by action type slug. */
2179
+ action_type?: string;
2180
+ /** Filter by country of origin ISO code. */
2181
+ country_of_origin?: string;
2182
+ /** Minimum year introduced (inclusive). */
2183
+ year_introduced_min?: number;
2184
+ /** Maximum year introduced (inclusive). */
2185
+ year_introduced_max?: number;
2186
+ /** Minimum empty weight in grams. */
2187
+ weight_min?: number;
2188
+ /** Maximum empty weight in grams. */
2189
+ weight_max?: number;
2190
+ /** Minimum barrel length in mm. */
2191
+ barrel_length_min?: number;
2192
+ /** Filter by production status. */
2193
+ status?: 'in_production' | 'discontinued' | 'prototype';
2194
+ /** Filter for firearms with/without a 3-D model. */
2195
+ has_3d_model?: 'true' | 'false';
2196
+ /** Comma-separated list of fields to include in the response (sparse fieldset). */
2197
+ fields?: string;
2198
+ /**
2199
+ * Sort column.
2200
+ * @defaultValue `"name"`
2201
+ */
2202
+ sort?: 'name' | 'weight' | 'year' | 'caliber' | 'created_at' | 'favorites';
2203
+ }
2204
+ /**
2205
+ * Parameters for `GET /v1/firearms/search`.
2206
+ */
2207
+ interface SearchFirearmsParams extends PaginationParams {
2208
+ /** Full-text search query (1-200 characters). */
2209
+ q: string;
2210
+ }
2211
+ /**
2212
+ * Parameters for `GET /v1/firearms/compare`.
2213
+ */
2214
+ interface CompareFirearmsParams {
2215
+ /**
2216
+ * Comma-separated firearm slugs to compare (maximum 5).
2217
+ *
2218
+ * @example `"glock-g17,beretta-92fs,sig-sauer-p226"`
2219
+ */
2220
+ ids: string;
2221
+ }
2222
+ /**
2223
+ * Parameters for `GET /v1/firearms/:id/game/meta`.
2224
+ */
2225
+ interface GameMetaParams {
2226
+ /** Filter by computed archetype. */
2227
+ archetype?: 'sniper' | 'assault' | 'tank' | 'glass-cannon' | 'all-rounder' | 'support' | 'stealth' | 'speedster';
2228
+ }
2229
+ /**
2230
+ * Parameters for `GET /v1/firearms/random`.
2231
+ */
2232
+ interface RandomFirearmParams {
2233
+ /** Filter by category slug. */
2234
+ category?: string;
2235
+ /** Filter by country of origin ISO code. */
2236
+ country?: string;
2237
+ }
2238
+ /**
2239
+ * Parameters for `GET /v1/firearms/top`.
2240
+ *
2241
+ * @example
2242
+ * ```ts
2243
+ * const params: TopFirearmsParams = { stat: 'lightest', category: 'semi-automatic-pistol', limit: 5 };
2244
+ * ```
2245
+ */
2246
+ interface TopFirearmsParams {
2247
+ /** The stat to rank by. */
2248
+ stat: 'lightest' | 'heaviest' | 'longest-range' | 'highest-rof' | 'most-compact' | 'highest-capacity' | 'most-powerful';
2249
+ /** Filter by category slug. */
2250
+ category?: string;
2251
+ /**
2252
+ * Maximum number of results (1-25).
2253
+ * @defaultValue `10`
2254
+ */
2255
+ limit?: number;
2256
+ }
2257
+ /**
2258
+ * Parameters for `GET /v1/firearms/head-to-head`.
2259
+ */
2260
+ interface HeadToHeadParams {
2261
+ /** Slug of firearm A. */
2262
+ a: string;
2263
+ /** Slug of firearm B. */
2264
+ b: string;
2265
+ }
2266
+ /**
2267
+ * Parameters for `GET /v1/firearms/by-feature`.
2268
+ */
2269
+ interface ByFeatureParams extends PaginationParams {
2270
+ /** Feature name to search for (matched via JSON LIKE). */
2271
+ feature: string;
2272
+ /** Optional category filter. */
2273
+ category?: string;
2274
+ }
2275
+ /**
2276
+ * Parameters for `GET /v1/firearms/by-action`.
2277
+ */
2278
+ interface ByActionParams extends PaginationParams {
2279
+ /** Action type to filter by (exact match). */
2280
+ action: string;
2281
+ }
2282
+ /**
2283
+ * Parameters for `GET /v1/firearms/by-material`.
2284
+ */
2285
+ interface ByMaterialParams extends PaginationParams {
2286
+ /** Material name to search for (substring match). */
2287
+ material: string;
2288
+ /** Which firearm component to search. */
2289
+ component: 'frame' | 'barrel' | 'stock' | 'slide';
2290
+ }
2291
+ /**
2292
+ * Parameters for `GET /v1/firearms/by-designer`.
2293
+ */
2294
+ interface ByDesignerParams extends PaginationParams {
2295
+ /** Designer name to search for (substring match). */
2296
+ designer: string;
2297
+ }
2298
+ /**
2299
+ * Parameters for `GET /v1/firearms/power-rating`.
2300
+ */
2301
+ interface PowerRatingParams extends PaginationParams {
2302
+ /** Optional category filter. */
2303
+ category?: string;
2304
+ }
2305
+ /**
2306
+ * Parameters for `GET /v1/firearms/timeline`.
2307
+ */
2308
+ interface TimelineParams {
2309
+ /**
2310
+ * Page number (1-based).
2311
+ * @defaultValue `1`
2312
+ */
2313
+ page?: number;
2314
+ /**
2315
+ * Items per page (1-100).
2316
+ * @defaultValue `50`
2317
+ */
2318
+ per_page?: number;
2319
+ /** Sort column. */
2320
+ sort?: string;
2321
+ /**
2322
+ * Sort direction.
2323
+ * @defaultValue `"asc"`
2324
+ */
2325
+ order?: 'asc' | 'desc';
2326
+ /** Earliest year to include (inclusive). */
2327
+ from?: number;
2328
+ /** Latest year to include (inclusive). */
2329
+ to?: number;
2330
+ /** Optional category filter. */
2331
+ category?: string;
2332
+ }
2333
+ /**
2334
+ * Parameters for `GET /v1/firearms/by-conflict`.
2335
+ */
2336
+ interface ByConflictParams extends PaginationParams {
2337
+ /** Conflict name to search for (substring match). */
2338
+ conflict: string;
2339
+ }
2340
+ /**
2341
+ * Parameters for `GET /v1/firearms/:id/calculate`.
2342
+ */
2343
+ interface CalculateBallisticsParams {
2344
+ /** Ammunition slug to calculate with. */
2345
+ ammo_id: string;
2346
+ }
2347
+ /**
2348
+ * Parameters for `GET /v1/firearms/:id/load`.
2349
+ */
2350
+ interface LoadFirearmParams {
2351
+ /**
2352
+ * Ammunition slug.
2353
+ * If omitted, uses the firearm's default ammo or the most common ammo
2354
+ * for the primary caliber.
2355
+ */
2356
+ ammo_id?: string;
2357
+ }
2358
+ /**
2359
+ * Parameters for `GET /v1/manufacturers`.
2360
+ */
2361
+ interface ListManufacturersParams extends PaginationParams {
2362
+ /** Filter by country ISO code. */
2363
+ country?: string;
2364
+ }
2365
+ /**
2366
+ * Parameters for `GET /v1/calibers`.
2367
+ */
2368
+ interface ListCalibersParams extends PaginationParams {
2369
+ /** Filter by cartridge type (e.g. `"centerfire_rifle"`). */
2370
+ cartridge_type?: string;
2371
+ /** Filter by primer type (e.g. `"Boxer"`). */
2372
+ primer_type?: string;
2373
+ }
2374
+ /**
2375
+ * Parameters for `GET /v1/calibers/compare`.
2376
+ */
2377
+ interface CompareCalibersParams {
2378
+ /**
2379
+ * Comma-separated caliber slugs to compare (maximum 5).
2380
+ *
2381
+ * @example `"9x19mm-parabellum,45-acp,40-s-w"`
2382
+ */
2383
+ ids: string;
2384
+ }
2385
+ /**
2386
+ * Parameters for `GET /v1/calibers/:id/ballistics`.
2387
+ */
2388
+ interface CaliberBallisticsParams {
2389
+ /**
2390
+ * Distance in metres for the ballistic calculation (1-5000).
2391
+ * @defaultValue `100`
2392
+ */
2393
+ distance?: number;
2394
+ }
2395
+ /**
2396
+ * Parameters for `GET /v1/ammunition`.
2397
+ */
2398
+ interface ListAmmunitionParams extends PaginationParams {
2399
+ /** Filter by caliber slug. */
2400
+ caliber_id?: string;
2401
+ /** Filter by bullet type (e.g. `"FMJ"`, `"JHP"`). */
2402
+ bullet_type?: string;
2403
+ /** Filter by common status (`0` = uncommon, `1` = common). */
2404
+ is_common?: 0 | 1;
2405
+ }
2406
+ /**
2407
+ * Parameters for `GET /v1/ammunition/:id/ballistics`.
2408
+ */
2409
+ interface AmmunitionBallisticsParams {
2410
+ /**
2411
+ * Barrel length in mm to use for the calculation (50-2000).
2412
+ * If omitted, uses the ammunition's reference barrel length.
2413
+ */
2414
+ barrel_length_mm?: number;
2415
+ /**
2416
+ * Comma-separated distances in metres for the trajectory table.
2417
+ * If omitted, uses the default set: 0, 50, 100, 200, 300, 400, 500, 600, 800, 1000.
2418
+ *
2419
+ * @example `"0,100,200,300,500"`
2420
+ */
2421
+ distances?: string;
2422
+ }
2423
+ /**
2424
+ * Parameters for `GET /v1/stats/popular-calibers`.
2425
+ */
2426
+ interface PopularCalibersParams {
2427
+ /**
2428
+ * Maximum number of results (1-100).
2429
+ * @defaultValue `20`
2430
+ */
2431
+ limit?: number;
2432
+ }
2433
+ /**
2434
+ * Parameters for `GET /v1/stats/prolific-manufacturers`.
2435
+ */
2436
+ interface ProlificManufacturersParams {
2437
+ /**
2438
+ * Maximum number of results (1-100).
2439
+ * @defaultValue `20`
2440
+ */
2441
+ limit?: number;
2442
+ /** Optional category filter. */
2443
+ category?: string;
2444
+ }
2445
+ /**
2446
+ * Parameters for `GET /v1/stats/by-era`.
2447
+ *
2448
+ * @example
2449
+ * ```ts
2450
+ * const params: ByEraParams = { decade: '1990s' };
2451
+ * ```
2452
+ */
2453
+ interface ByEraParams {
2454
+ /** Decade string in format `"1990s"`, `"2000s"`, etc. */
2455
+ decade: string;
2456
+ }
2457
+ /**
2458
+ * Parameters for `GET /v1/stats/adoption/country`.
2459
+ */
2460
+ interface AdoptionByCountryParams {
2461
+ /** ISO 3166-1 country code. */
2462
+ code: string;
2463
+ }
2464
+ /**
2465
+ * Parameters for `GET /v1/stats/adoption/type`.
2466
+ */
2467
+ interface AdoptionByTypeParams {
2468
+ /** User type to query. */
2469
+ type: 'military' | 'law_enforcement' | 'civilian' | 'paramilitary' | 'special_forces' | 'private_security' | 'training';
2470
+ }
2471
+ /**
2472
+ * Parameters for `GET /v1/stats/action-types`.
2473
+ */
2474
+ interface ActionTypesParams {
2475
+ /** Optional category filter. */
2476
+ category?: string;
2477
+ }
2478
+ /**
2479
+ * Parameters for `GET /v1/stats/feature-frequency`.
2480
+ */
2481
+ interface FeatureFrequencyParams {
2482
+ /** Optional category filter. */
2483
+ category?: string;
2484
+ /**
2485
+ * Maximum number of results (1-100).
2486
+ * @defaultValue `50`
2487
+ */
2488
+ limit?: number;
2489
+ }
2490
+ /**
2491
+ * Parameters for `GET /v1/stats/caliber-popularity-by-era`.
2492
+ */
2493
+ interface CaliberPopularityByEraParams {
2494
+ /** Starting decade (inclusive, e.g. `"1950s"`). */
2495
+ from_decade?: string;
2496
+ /** Ending decade (inclusive, e.g. `"2020s"`). */
2497
+ to_decade?: string;
2498
+ }
2499
+ /**
2500
+ * Parameters for `GET /v1/game/balance`.
2501
+ */
2502
+ interface BalanceReportParams {
2503
+ /**
2504
+ * Z-score threshold for flagging outliers (0-100).
2505
+ * @defaultValue `20`
2506
+ */
2507
+ threshold?: number;
2508
+ }
2509
+ /**
2510
+ * Parameters for `GET /v1/game/tier-list`.
2511
+ */
2512
+ interface TierListParams {
2513
+ /** Optional category filter. */
2514
+ category?: string;
2515
+ /**
2516
+ * Stat to rank by.
2517
+ * @defaultValue `"damage"`
2518
+ */
2519
+ stat?: 'damage' | 'accuracy' | 'range' | 'fireRate' | 'mobility' | 'recoilControl' | 'reloadSpeed' | 'concealment';
2520
+ }
2521
+ /**
2522
+ * Parameters for `GET /v1/game/matchups`.
2523
+ */
2524
+ interface MatchupsParams {
2525
+ /** Slug of firearm A. */
2526
+ a: string;
2527
+ /** Slug of firearm B. */
2528
+ b: string;
2529
+ }
2530
+ /**
2531
+ * Parameters for `GET /v1/game/role-roster`.
2532
+ */
2533
+ interface RoleRosterParams {
2534
+ /** The role to build a roster for. */
2535
+ role: 'sniper' | 'assault' | 'tank' | 'support' | 'stealth' | 'speedster';
2536
+ /**
2537
+ * Number of firearms to return (1-25).
2538
+ * @defaultValue `5`
2539
+ */
2540
+ count?: number;
2541
+ }
2542
+ /**
2543
+ * Parameters for `GET /v1/game/stat-distribution`.
2544
+ */
2545
+ interface StatDistributionParams {
2546
+ /** The game stat to analyse. */
2547
+ stat: 'damage' | 'accuracy' | 'range' | 'fireRate' | 'mobility' | 'recoilControl' | 'reloadSpeed' | 'concealment';
2548
+ }
2549
+ /**
2550
+ * Parameters for `GET /v1/game-stats/:version/firearms`.
2551
+ */
2552
+ interface ListSnapshotFirearmsParams extends PaginationParams {
2553
+ }
2554
+ /**
2555
+ * Parameters for `GET /v1/data/confidence`.
2556
+ */
2557
+ interface ConfidenceParams extends PaginationParams {
2558
+ /**
2559
+ * Return firearms with confidence below this threshold (0.0-1.0).
2560
+ * @defaultValue `0.5`
2561
+ */
2562
+ below?: number;
2563
+ }
2564
+ /**
2565
+ * Parameters for `GET /v1/firearms/:id/silhouette`.
2566
+ */
2567
+ interface SilhouetteParams {
2568
+ /** Output format: `"svg"` (raw), `"datauri"` (base64), or `"json"` (metadata). */
2569
+ format?: 'svg' | 'datauri' | 'json';
2570
+ /** Stroke width in pixels. */
2571
+ stroke_width?: number;
2572
+ /** Stroke color (CSS color string). */
2573
+ stroke_color?: string;
2574
+ }
2575
+ /**
2576
+ * Parameters for `GET /v1/me/favorites`.
2577
+ */
2578
+ interface ListFavoritesParams {
2579
+ /** Page number (1-based). */
2580
+ page?: number;
2581
+ /** Items per page (1-100). */
2582
+ limit?: number;
2583
+ }
2584
+ /**
2585
+ * Body for `POST /v1/me/reports`.
2586
+ */
2587
+ interface CreateReportParams {
2588
+ /** Firearm slug to report on. */
2589
+ firearmId: string;
2590
+ /** Section of the firearm data. */
2591
+ section: 'game-stats' | 'specs' | 'ballistics' | 'barrel' | 'materials' | 'operating' | 'calibers' | 'users' | 'description';
2592
+ /** Type of data issue. */
2593
+ issueType: 'incorrect' | 'missing' | 'outdated';
2594
+ /** Description of the issue (10-2000 chars). */
2595
+ description: string;
2596
+ /** Optional reference URLs (max 5). */
2597
+ references?: string[];
2598
+ /** Optional suggested correction (max 1000 chars). */
2599
+ suggestedValue?: string;
2600
+ }
2601
+ /**
2602
+ * Parameters for `GET /v1/me/reports`.
2603
+ */
2604
+ interface ListReportsParams {
2605
+ /** Page number (1-based). */
2606
+ page?: number;
2607
+ /** Items per page (1-100). */
2608
+ per_page?: number;
2609
+ }
2610
+ /**
2611
+ * Body for `POST /v1/me/support`.
2612
+ */
2613
+ interface CreateTicketParams {
2614
+ /** Ticket subject (1-200 chars). */
2615
+ subject: string;
2616
+ /** Ticket description (1-5000 chars). */
2617
+ description: string;
2618
+ /** Ticket category. */
2619
+ category?: 'billing' | 'technical' | 'data_quality' | 'feature_request' | 'other';
2620
+ /** Priority level. */
2621
+ priority?: 'low' | 'normal' | 'high' | 'urgent';
2622
+ }
2623
+ /**
2624
+ * Parameters for `GET /v1/me/support`.
2625
+ */
2626
+ interface ListTicketsParams {
2627
+ /** Page number (1-based). */
2628
+ page?: number;
2629
+ /** Items per page (1-100). */
2630
+ per_page?: number;
2631
+ /** Filter by status. */
2632
+ status?: 'open' | 'in_progress' | 'waiting' | 'resolved' | 'closed';
2633
+ /** Sort column. */
2634
+ sort?: 'created_at' | 'updated_at' | 'status' | 'priority';
2635
+ /** Sort direction. */
2636
+ order?: 'asc' | 'desc';
2637
+ }
2638
+ /**
2639
+ * Body for `POST /v1/me/support/:ticketId/replies`.
2640
+ */
2641
+ interface CreateReplyParams {
2642
+ /** Reply message (1-5000 chars). */
2643
+ message: string;
2644
+ }
2645
+ /**
2646
+ * Body for `POST /v1/me/webhooks`.
2647
+ */
2648
+ interface CreateWebhookEndpointParams {
2649
+ /** Delivery URL. */
2650
+ url: string;
2651
+ /** Optional description. */
2652
+ description?: string;
2653
+ /** Event types to subscribe to (at least one). */
2654
+ events: string[];
2655
+ }
2656
+ /**
2657
+ * Body for `PUT /v1/me/webhooks/:id`.
2658
+ */
2659
+ interface UpdateWebhookEndpointParams {
2660
+ /** New delivery URL. */
2661
+ url?: string;
2662
+ /** Updated description. */
2663
+ description?: string | null;
2664
+ /** Updated event subscriptions. */
2665
+ events?: string[];
2666
+ /** Whether the endpoint is active. */
2667
+ active?: boolean;
2668
+ }
2669
+ /**
2670
+ * Parameters for `GET /v1/me/webhooks`.
2671
+ */
2672
+ interface ListWebhookEndpointsParams {
2673
+ /** Page number (1-based). */
2674
+ page?: number;
2675
+ /** Items per page (1-100). */
2676
+ per_page?: number;
2677
+ }
2678
+ /**
2679
+ * Parameters for `GET /v1/me/usage`.
2680
+ */
2681
+ interface UsageParams {
2682
+ /** Month in YYYY-MM format. */
2683
+ month?: string;
2684
+ /** Number of days for daily breakdown (1-90). */
2685
+ days?: number;
2686
+ }
2687
+
2688
+ /**
2689
+ * The error object nested inside an {@link APIErrorResponse}.
2690
+ *
2691
+ * @example
2692
+ * ```ts
2693
+ * const body: ErrorBody = {
2694
+ * code: 'NOT_FOUND',
2695
+ * message: "Firearm 'nonexistent-slug' not found",
2696
+ * };
2697
+ * ```
2698
+ */
2699
+ interface ErrorBody {
2700
+ /**
2701
+ * Machine-readable error code.
2702
+ *
2703
+ * Known codes:
2704
+ * - `"NOT_FOUND"` -- Resource does not exist (HTTP 404).
2705
+ * - `"UNAUTHORIZED"` -- Authentication required (HTTP 401).
2706
+ * - `"FORBIDDEN"` -- Insufficient permissions (HTTP 403).
2707
+ * - `"RATE_LIMITED"` -- Too many requests (HTTP 429).
2708
+ * - `"VALIDATION_ERROR"` -- Request parameters failed validation (HTTP 400).
2709
+ */
2710
+ code: string;
2711
+ /** Human-readable error message. */
2712
+ message: string;
2713
+ /**
2714
+ * Optional validation details.
2715
+ *
2716
+ * Present on `VALIDATION_ERROR` responses; shape varies by endpoint.
2717
+ */
2718
+ details?: Record<string, unknown>;
2719
+ }
2720
+ /**
2721
+ * Standard API error response envelope.
2722
+ *
2723
+ * All non-2xx responses from the GunSpec.io API return this shape.
2724
+ *
2725
+ * @example
2726
+ * ```ts
2727
+ * // 404 response body
2728
+ * const response: APIErrorResponse = {
2729
+ * success: false,
2730
+ * error: {
2731
+ * code: 'NOT_FOUND',
2732
+ * message: "Firearm 'nonexistent-slug' not found",
2733
+ * },
2734
+ * };
2735
+ * ```
2736
+ */
2737
+ interface APIErrorResponse {
2738
+ /** Always `false` for error responses. */
2739
+ success: false;
2740
+ /** The error details. */
2741
+ error: ErrorBody;
2742
+ }
2743
+
2744
+ /**
2745
+ * Firearms resource for the GunSpec SDK.
2746
+ *
2747
+ * Provides access to all `/v1/firearms` endpoints including listing,
2748
+ * searching, comparing, filtering, and retrieving individual firearm
2749
+ * details with sub-resources (images, variants, game stats, etc.).
2750
+ *
2751
+ * @module
2752
+ */
2753
+
2754
+ /**
2755
+ * Resource class for interacting with the GunSpec Firearms API.
2756
+ *
2757
+ * Wraps all `/v1/firearms` endpoints. Instantiated internally by the
2758
+ * {@link GunSpec} client and exposed as `client.firearms`.
2759
+ *
2760
+ * @example
2761
+ * ```typescript
2762
+ * import GunSpec from '@gunspec/sdk';
2763
+ *
2764
+ * const client = new GunSpec();
2765
+ *
2766
+ * // List firearms with filters
2767
+ * const { data, pagination } = await client.firearms.list({
2768
+ * manufacturer: 'glock',
2769
+ * category: 'pistol',
2770
+ * per_page: 10,
2771
+ * });
2772
+ *
2773
+ * // Get a single firearm by slug
2774
+ * const { data: firearm } = await client.firearms.get('glock-g17');
2775
+ * ```
2776
+ */
2777
+ declare class FirearmsResource {
2778
+ private readonly client;
2779
+ constructor(client: HttpClient);
2780
+ /**
2781
+ * List firearms with optional filters and pagination.
2782
+ *
2783
+ * @param params - Optional query parameters for filtering, sorting, and pagination.
2784
+ * @returns A paginated list of firearms matching the given filters.
2785
+ * @throws {BadRequestError} If any filter value is invalid.
2786
+ * @throws {AuthenticationError} If the API key is missing or invalid.
2787
+ *
2788
+ * @example
2789
+ * ```typescript
2790
+ * const result = await client.firearms.list({
2791
+ * manufacturer: 'beretta',
2792
+ * status: 'in_production',
2793
+ * sort: 'name',
2794
+ * order: 'asc',
2795
+ * page: 1,
2796
+ * per_page: 25,
2797
+ * });
2798
+ * console.log(result.data); // Firearm[]
2799
+ * console.log(result.pagination.totalPages);
2800
+ * ```
2801
+ */
2802
+ list(params?: ListFirearmsParams): Promise<PaginatedResponse<Firearm>>;
2803
+ /**
2804
+ * Auto-paginate through all firearms matching the given filters.
2805
+ *
2806
+ * Returns an async iterator that fetches pages on demand, yielding
2807
+ * individual {@link Firearm} objects. Useful for processing large
2808
+ * result sets without managing pagination manually.
2809
+ *
2810
+ * @param params - Optional query parameters for filtering and sorting.
2811
+ * @returns An async iterable iterator yielding individual firearms.
2812
+ *
2813
+ * @example
2814
+ * ```typescript
2815
+ * for await (const firearm of client.firearms.listAutoPaging({ manufacturer: 'colt' })) {
2816
+ * console.log(firearm.name);
2817
+ * }
2818
+ * ```
2819
+ */
2820
+ listAutoPaging(params?: ListFirearmsParams): AsyncIterableIterator<Firearm>;
2821
+ /**
2822
+ * Full-text search across firearms.
2823
+ *
2824
+ * @param params - Search query and pagination parameters.
2825
+ * @returns A paginated list of firearms matching the search query.
2826
+ * @throws {BadRequestError} If the search query is empty or too long.
2827
+ * @throws {AuthenticationError} If the API key is missing or invalid.
2828
+ *
2829
+ * @example
2830
+ * ```typescript
2831
+ * const result = await client.firearms.search({ q: '9mm compact' });
2832
+ * console.log(result.data.length);
2833
+ * ```
2834
+ */
2835
+ search(params: SearchFirearmsParams): Promise<PaginatedResponse<Firearm>>;
2836
+ /**
2837
+ * Compare up to 5 firearms side by side.
2838
+ *
2839
+ * @param params - Object containing comma-separated firearm IDs.
2840
+ * @returns An array of firearms with full details for comparison.
2841
+ * @throws {BadRequestError} If more than 5 IDs are provided or any ID is invalid.
2842
+ * @throws {NotFoundError} If any of the specified firearms do not exist.
2843
+ *
2844
+ * @example
2845
+ * ```typescript
2846
+ * const { data } = await client.firearms.compare({ ids: 'glock-g17,sig-sauer-p320,beretta-92fs' });
2847
+ * console.log(data.items.length); // 3
2848
+ * ```
2849
+ */
2850
+ compare(params: CompareFirearmsParams): Promise<APIResponse<FirearmComparison>>;
2851
+ /**
2852
+ * Retrieve game metadata for firearms (archetypes, stat ranges, etc.).
2853
+ *
2854
+ * @param params - Optional archetype filter.
2855
+ * @returns Game metadata including archetype definitions and stat ranges.
2856
+ * @throws {AuthenticationError} If the API key is missing or invalid.
2857
+ *
2858
+ * @example
2859
+ * ```typescript
2860
+ * const { data } = await client.firearms.gameMeta({ archetype: 'sniper' });
2861
+ * ```
2862
+ */
2863
+ gameMeta(params?: GameMetaParams): Promise<APIResponse<GameMetaItem[]>>;
2864
+ /**
2865
+ * List all known action types across the database.
2866
+ *
2867
+ * @returns An array of distinct action type strings.
2868
+ * @throws {AuthenticationError} If the API key is missing or invalid.
2869
+ *
2870
+ * @example
2871
+ * ```typescript
2872
+ * const { data } = await client.firearms.actionTypes();
2873
+ * // ['Semi-automatic', 'Bolt action', 'Lever action', ...]
2874
+ * ```
2875
+ */
2876
+ actionTypes(): Promise<APIResponse<ActionTypeStats[]>>;
2877
+ /**
2878
+ * Get available filter options for the firearms list endpoint.
2879
+ *
2880
+ * Returns distinct values for manufacturers, calibers, categories,
2881
+ * action types, countries, and statuses that can be used as filter values.
2882
+ *
2883
+ * @returns An object mapping filter fields to their available values.
2884
+ * @throws {AuthenticationError} If the API key is missing or invalid.
2885
+ *
2886
+ * @example
2887
+ * ```typescript
2888
+ * const { data } = await client.firearms.filterOptions();
2889
+ * console.log(data.manufacturers); // ['beretta', 'colt', ...]
2890
+ * console.log(data.categories); // ['pistol', 'rifle', ...]
2891
+ * ```
2892
+ */
2893
+ filterOptions(): Promise<APIResponse<FilterOptions>>;
2894
+ /**
2895
+ * Get a random firearm, optionally filtered by category or country.
2896
+ *
2897
+ * @param params - Optional category and country filters.
2898
+ * @returns A single randomly selected firearm.
2899
+ * @throws {AuthenticationError} If the API key is missing or invalid.
2900
+ *
2901
+ * @example
2902
+ * ```typescript
2903
+ * const { data } = await client.firearms.random({ category: 'pistol' });
2904
+ * console.log(data.name);
2905
+ * ```
2906
+ */
2907
+ random(params?: RandomFirearmParams): Promise<APIResponse<Firearm>>;
2908
+ /**
2909
+ * Get top firearms ranked by a specific statistic.
2910
+ *
2911
+ * @param params - The stat to rank by and optional category/limit filters.
2912
+ * @returns An ordered array of firearms ranked by the specified stat.
2913
+ * @throws {BadRequestError} If the stat value is not a recognized ranking metric.
2914
+ * @throws {AuthenticationError} If the API key is missing or invalid.
2915
+ *
2916
+ * @example
2917
+ * ```typescript
2918
+ * const { data } = await client.firearms.top({
2919
+ * stat: 'lightest',
2920
+ * category: 'pistol',
2921
+ * limit: 5,
2922
+ * });
2923
+ * ```
2924
+ */
2925
+ top(params: TopFirearmsParams): Promise<APIResponse<Firearm[]>>;
2926
+ /**
2927
+ * Compare two firearms in a head-to-head matchup.
2928
+ *
2929
+ * @param params - The slugs of the two firearms to compare.
2930
+ * @returns A detailed head-to-head comparison with per-stat breakdowns.
2931
+ * @throws {NotFoundError} If either firearm does not exist.
2932
+ * @throws {AuthenticationError} If the API key is missing or invalid.
2933
+ *
2934
+ * @example
2935
+ * ```typescript
2936
+ * const { data } = await client.firearms.headToHead({ a: 'glock-g17', b: 'sig-sauer-p320' });
2937
+ * console.log(data.winner);
2938
+ * ```
2939
+ */
2940
+ headToHead(params: HeadToHeadParams): Promise<APIResponse<HeadToHead>>;
2941
+ /**
2942
+ * Filter firearms by a specific feature.
2943
+ *
2944
+ * @param params - The feature name and optional category filter with pagination.
2945
+ * @returns A paginated list of firearms that have the specified feature.
2946
+ * @throws {BadRequestError} If the feature name is empty.
2947
+ * @throws {AuthenticationError} If the API key is missing or invalid.
2948
+ *
2949
+ * @example
2950
+ * ```typescript
2951
+ * const result = await client.firearms.byFeature({ feature: 'threaded-barrel' });
2952
+ * ```
2953
+ */
2954
+ byFeature(params: ByFeatureParams): Promise<PaginatedResponse<Firearm>>;
2955
+ /**
2956
+ * Filter firearms by action type.
2957
+ *
2958
+ * @param params - The action type string with pagination.
2959
+ * @returns A paginated list of firearms with the specified action type.
2960
+ * @throws {BadRequestError} If the action type is empty.
2961
+ * @throws {AuthenticationError} If the API key is missing or invalid.
2962
+ *
2963
+ * @example
2964
+ * ```typescript
2965
+ * const result = await client.firearms.byAction({ action: 'semi-automatic' });
2966
+ * ```
2967
+ */
2968
+ byAction(params: ByActionParams): Promise<PaginatedResponse<Firearm>>;
2969
+ /**
2970
+ * Filter firearms by frame/component material.
2971
+ *
2972
+ * @param params - The material name, component type, and pagination.
2973
+ * @returns A paginated list of firearms using the specified material.
2974
+ * @throws {BadRequestError} If the material or component is invalid.
2975
+ * @throws {AuthenticationError} If the API key is missing or invalid.
2976
+ *
2977
+ * @example
2978
+ * ```typescript
2979
+ * const result = await client.firearms.byMaterial({
2980
+ * material: 'polymer',
2981
+ * component: 'frame',
2982
+ * });
2983
+ * ```
2984
+ */
2985
+ byMaterial(params: ByMaterialParams): Promise<PaginatedResponse<Firearm>>;
2986
+ /**
2987
+ * Filter firearms by designer name.
2988
+ *
2989
+ * @param params - The designer name string with pagination.
2990
+ * @returns A paginated list of firearms designed by the specified person.
2991
+ * @throws {BadRequestError} If the designer name is empty.
2992
+ * @throws {AuthenticationError} If the API key is missing or invalid.
2993
+ *
2994
+ * @example
2995
+ * ```typescript
2996
+ * const result = await client.firearms.byDesigner({ designer: 'John Browning' });
2997
+ * ```
2998
+ */
2999
+ byDesigner(params: ByDesignerParams): Promise<PaginatedResponse<Firearm>>;
3000
+ /**
3001
+ * Get firearms ranked by computed power rating.
3002
+ *
3003
+ * @param params - Optional category filter with pagination.
3004
+ * @returns A paginated list of firearms with their power ratings.
3005
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3006
+ *
3007
+ * @example
3008
+ * ```typescript
3009
+ * const result = await client.firearms.powerRating({ category: 'rifle' });
3010
+ * for (const item of result.data) {
3011
+ * console.log(`${item.name}: ${item.rating}`);
3012
+ * }
3013
+ * ```
3014
+ */
3015
+ powerRating(params?: PowerRatingParams): Promise<PaginatedResponse<PowerRating>>;
3016
+ /**
3017
+ * Get firearms arranged in a chronological timeline.
3018
+ *
3019
+ * @param params - Optional year range, category filter, and pagination.
3020
+ * @returns A paginated list of firearms ordered by introduction date.
3021
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3022
+ *
3023
+ * @example
3024
+ * ```typescript
3025
+ * const result = await client.firearms.timeline({ from: 1900, to: 1950 });
3026
+ * ```
3027
+ */
3028
+ timeline(params?: TimelineParams): Promise<PaginatedResponse<Firearm>>;
3029
+ /**
3030
+ * Filter firearms by military conflict.
3031
+ *
3032
+ * @param params - The conflict identifier string with pagination.
3033
+ * @returns A paginated list of firearms used in the specified conflict.
3034
+ * @throws {BadRequestError} If the conflict name is empty.
3035
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3036
+ *
3037
+ * @example
3038
+ * ```typescript
3039
+ * const result = await client.firearms.byConflict({ conflict: 'world-war-ii' });
3040
+ * ```
3041
+ */
3042
+ byConflict(params: ByConflictParams): Promise<PaginatedResponse<Firearm>>;
3043
+ /**
3044
+ * Get a single firearm by its slug or ID.
3045
+ *
3046
+ * @param id - The firearm slug (e.g. `"glock-g17"`) or numeric ID.
3047
+ * @returns The full firearm record with all available fields.
3048
+ * @throws {NotFoundError} If no firearm matches the given identifier.
3049
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3050
+ *
3051
+ * @example
3052
+ * ```typescript
3053
+ * const { data } = await client.firearms.get('beretta-92fs');
3054
+ * console.log(data.name); // "Beretta 92FS"
3055
+ * console.log(data.manufacturerId); // "beretta"
3056
+ * ```
3057
+ */
3058
+ get(id: string): Promise<APIResponse<Firearm>>;
3059
+ /**
3060
+ * Get all variants of a firearm.
3061
+ *
3062
+ * @param id - The parent firearm slug or ID.
3063
+ * @returns An array of variant firearms derived from the parent.
3064
+ * @throws {NotFoundError} If the parent firearm does not exist.
3065
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3066
+ *
3067
+ * @example
3068
+ * ```typescript
3069
+ * const { data } = await client.firearms.getVariants('colt-1911');
3070
+ * console.log(data.map(v => v.name));
3071
+ * ```
3072
+ */
3073
+ getVariants(id: string): Promise<APIResponse<Firearm[]>>;
3074
+ /**
3075
+ * Get images for a firearm.
3076
+ *
3077
+ * @param id - The firearm slug or ID.
3078
+ * @returns An array of image records with URLs, dimensions, and metadata.
3079
+ * @throws {NotFoundError} If the firearm does not exist.
3080
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3081
+ *
3082
+ * @example
3083
+ * ```typescript
3084
+ * const { data } = await client.firearms.getImages('sig-sauer-p226');
3085
+ * for (const img of data) {
3086
+ * console.log(img.url, img.width, img.height);
3087
+ * }
3088
+ * ```
3089
+ */
3090
+ getImages(id: string): Promise<APIResponse<FirearmImage[]>>;
3091
+ /**
3092
+ * Get computed game statistics for a firearm.
3093
+ *
3094
+ * @param id - The firearm slug or ID.
3095
+ * @returns Game-balanced stats including damage, accuracy, range, etc.
3096
+ * @throws {NotFoundError} If the firearm does not exist.
3097
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3098
+ *
3099
+ * @example
3100
+ * ```typescript
3101
+ * const { data } = await client.firearms.getGameStats('ak-47');
3102
+ * console.log(data.damage, data.accuracy, data.range);
3103
+ * ```
3104
+ */
3105
+ getGameStats(id: string): Promise<APIResponse<GameStats>>;
3106
+ /**
3107
+ * Get physical dimensions for a firearm.
3108
+ *
3109
+ * @param id - The firearm slug or ID.
3110
+ * @returns Detailed dimension data (length, height, width, barrel length, weight).
3111
+ * @throws {NotFoundError} If the firearm does not exist.
3112
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3113
+ *
3114
+ * @example
3115
+ * ```typescript
3116
+ * const { data } = await client.firearms.getDimensions('glock-g19');
3117
+ * console.log(`${data.overallLengthMm}mm overall`);
3118
+ * ```
3119
+ */
3120
+ getDimensions(id: string): Promise<APIResponse<Dimensions>>;
3121
+ /**
3122
+ * Get known military/law-enforcement adopters of a firearm.
3123
+ *
3124
+ * @param id - The firearm slug or ID.
3125
+ * @returns An array of users/adopters with country and organization data.
3126
+ * @throws {NotFoundError} If the firearm does not exist.
3127
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3128
+ *
3129
+ * @example
3130
+ * ```typescript
3131
+ * const { data } = await client.firearms.getUsers('beretta-m9');
3132
+ * for (const user of data) {
3133
+ * console.log(user.country, user.organization);
3134
+ * }
3135
+ * ```
3136
+ */
3137
+ getUsers(id: string): Promise<APIResponse<FirearmUser[]>>;
3138
+ /**
3139
+ * Get the family tree (lineage) of a firearm.
3140
+ *
3141
+ * @param id - The firearm slug or ID.
3142
+ * @returns A tree structure showing predecessors, descendants, and related models.
3143
+ * @throws {NotFoundError} If the firearm does not exist.
3144
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3145
+ *
3146
+ * @example
3147
+ * ```typescript
3148
+ * const { data } = await client.firearms.getFamilyTree('m16a2');
3149
+ * console.log(data.ancestors, data.descendants);
3150
+ * ```
3151
+ */
3152
+ getFamilyTree(id: string): Promise<APIResponse<FamilyTree>>;
3153
+ /**
3154
+ * Get firearms similar to a given firearm.
3155
+ *
3156
+ * @param id - The firearm slug or ID.
3157
+ * @returns An array of similar firearms ranked by similarity score.
3158
+ * @throws {NotFoundError} If the firearm does not exist.
3159
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3160
+ *
3161
+ * @example
3162
+ * ```typescript
3163
+ * const { data } = await client.firearms.getSimilar('glock-g17');
3164
+ * for (const match of data) {
3165
+ * console.log(match.name, match.similarityScore);
3166
+ * }
3167
+ * ```
3168
+ */
3169
+ getSimilar(id: string): Promise<APIResponse<SimilarFirearm[]>>;
3170
+ /**
3171
+ * Get the worldwide adoption map for a firearm.
3172
+ *
3173
+ * @param id - The firearm slug or ID.
3174
+ * @returns Adoption data by country with usage type and date ranges.
3175
+ * @throws {NotFoundError} If the firearm does not exist.
3176
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3177
+ *
3178
+ * @example
3179
+ * ```typescript
3180
+ * const { data } = await client.firearms.getAdoptionMap('fn-fal');
3181
+ * console.log(data.countries.length, 'countries adopted this firearm');
3182
+ * ```
3183
+ */
3184
+ getAdoptionMap(id: string): Promise<APIResponse<AdoptionMap>>;
3185
+ /**
3186
+ * Get the full game profile for a firearm.
3187
+ *
3188
+ * @param id - The firearm slug or ID.
3189
+ * @returns Game-specific profile including archetype, tier, and stat breakdown.
3190
+ * @throws {NotFoundError} If the firearm does not exist.
3191
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3192
+ *
3193
+ * @example
3194
+ * ```typescript
3195
+ * const { data } = await client.firearms.getGameProfile('mp5');
3196
+ * console.log(data.archetype, data.tier);
3197
+ * ```
3198
+ */
3199
+ getGameProfile(id: string): Promise<APIResponse<GameProfile>>;
3200
+ /**
3201
+ * Get an SVG silhouette (line art) of a firearm.
3202
+ *
3203
+ * @param id - The firearm slug or ID.
3204
+ * @param params - Optional format and stroke customization parameters.
3205
+ * @returns Silhouette data in the requested format (raw SVG, data URI, or JSON).
3206
+ * @throws {NotFoundError} If the firearm does not exist or has no silhouette.
3207
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3208
+ *
3209
+ * @example
3210
+ * ```typescript
3211
+ * const { data } = await client.firearms.getSilhouette('ak-47', {
3212
+ * format: 'datauri',
3213
+ * stroke_width: 2,
3214
+ * stroke_color: '#333',
3215
+ * });
3216
+ * // Use data.dataUri in an <img> tag
3217
+ * ```
3218
+ */
3219
+ getSilhouette(id: string, params?: SilhouetteParams): Promise<APIResponse<Silhouette>>;
3220
+ /**
3221
+ * Calculate ballistics for a firearm with specific ammunition.
3222
+ *
3223
+ * @param id - The firearm slug or ID.
3224
+ * @param params - Ammunition ID and optional ballistic parameters.
3225
+ * @returns Calculated ballistic data including velocity, energy, and trajectory.
3226
+ * @throws {NotFoundError} If the firearm or ammunition does not exist.
3227
+ * @throws {BadRequestError} If the ammunition is incompatible with the firearm.
3228
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3229
+ *
3230
+ * @example
3231
+ * ```typescript
3232
+ * const { data } = await client.firearms.calculate('glock-g17', {
3233
+ * ammo_id: '9mm-federal-hst-124gr',
3234
+ * });
3235
+ * console.log(data.muzzleVelocity, data.muzzleEnergy);
3236
+ * ```
3237
+ */
3238
+ calculate(id: string, params: CalculateBallisticsParams): Promise<APIResponse<FirearmCalculation>>;
3239
+ /**
3240
+ * Load a firearm with ammunition and get combined performance data.
3241
+ *
3242
+ * @param id - The firearm slug or ID.
3243
+ * @param params - Optional ammunition ID to load.
3244
+ * @returns Combined firearm + ammunition data with computed performance metrics.
3245
+ * @throws {NotFoundError} If the firearm does not exist.
3246
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3247
+ *
3248
+ * @example
3249
+ * ```typescript
3250
+ * const { data } = await client.firearms.load('sig-sauer-p226', {
3251
+ * ammo_id: '9mm-winchester-ranger-147gr',
3252
+ * });
3253
+ * ```
3254
+ */
3255
+ load(id: string, params?: LoadFirearmParams): Promise<APIResponse<FirearmLoadProfile>>;
3256
+ }
3257
+
3258
+ /**
3259
+ * Manufacturers resource for the GunSpec SDK.
3260
+ *
3261
+ * Provides access to all `/v1/manufacturers` endpoints including listing,
3262
+ * retrieving details, and exploring a manufacturer's firearms catalog,
3263
+ * timeline, and statistics.
3264
+ *
3265
+ * @module
3266
+ */
3267
+
3268
+ /**
3269
+ * Resource class for interacting with the GunSpec Manufacturers API.
3270
+ *
3271
+ * Wraps all `/v1/manufacturers` endpoints. Instantiated internally by the
3272
+ * {@link GunSpec} client and exposed as `client.manufacturers`.
3273
+ *
3274
+ * @example
3275
+ * ```typescript
3276
+ * import GunSpec from '@gunspec/sdk';
3277
+ *
3278
+ * const client = new GunSpec();
3279
+ *
3280
+ * // List manufacturers
3281
+ * const { data } = await client.manufacturers.list({ country: 'US' });
3282
+ *
3283
+ * // Get manufacturer details
3284
+ * const { data: mfr } = await client.manufacturers.get('beretta');
3285
+ * ```
3286
+ */
3287
+ declare class ManufacturersResource {
3288
+ private readonly client;
3289
+ constructor(client: HttpClient);
3290
+ /**
3291
+ * List manufacturers with optional filters and pagination.
3292
+ *
3293
+ * @param params - Optional query parameters for filtering by country, sorting, and pagination.
3294
+ * @returns A paginated list of manufacturers.
3295
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3296
+ *
3297
+ * @example
3298
+ * ```typescript
3299
+ * const result = await client.manufacturers.list({
3300
+ * country: 'DE',
3301
+ * sort: 'name',
3302
+ * order: 'asc',
3303
+ * per_page: 50,
3304
+ * });
3305
+ * ```
3306
+ */
3307
+ list(params?: ListManufacturersParams): Promise<PaginatedResponse<Manufacturer>>;
3308
+ /**
3309
+ * Auto-paginate through all manufacturers matching the given filters.
3310
+ *
3311
+ * Returns an async iterator that fetches pages on demand, yielding
3312
+ * individual {@link Manufacturer} objects.
3313
+ *
3314
+ * @param params - Optional query parameters for filtering and sorting.
3315
+ * @returns An async iterable iterator yielding individual manufacturers.
3316
+ *
3317
+ * @example
3318
+ * ```typescript
3319
+ * for await (const mfr of client.manufacturers.listAutoPaging()) {
3320
+ * console.log(mfr.name, mfr.country);
3321
+ * }
3322
+ * ```
3323
+ */
3324
+ listAutoPaging(params?: ListManufacturersParams): AsyncIterableIterator<Manufacturer>;
3325
+ /**
3326
+ * Get a single manufacturer by its slug or ID.
3327
+ *
3328
+ * @param id - The manufacturer slug (e.g. `"beretta"`) or numeric ID.
3329
+ * @returns The full manufacturer record.
3330
+ * @throws {NotFoundError} If no manufacturer matches the given identifier.
3331
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3332
+ *
3333
+ * @example
3334
+ * ```typescript
3335
+ * const { data } = await client.manufacturers.get('sig-sauer');
3336
+ * console.log(data.name, data.foundedYear, data.country);
3337
+ * ```
3338
+ */
3339
+ get(id: string): Promise<APIResponse<Manufacturer>>;
3340
+ /**
3341
+ * Get all firearms produced by a manufacturer.
3342
+ *
3343
+ * @param id - The manufacturer slug or ID.
3344
+ * @param params - Optional pagination parameters.
3345
+ * @returns A paginated list of firearms from the specified manufacturer.
3346
+ * @throws {NotFoundError} If the manufacturer does not exist.
3347
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3348
+ *
3349
+ * @example
3350
+ * ```typescript
3351
+ * const result = await client.manufacturers.getFirearms('colt', { per_page: 50 });
3352
+ * console.log(`Colt makes ${result.pagination.total} firearms`);
3353
+ * ```
3354
+ */
3355
+ getFirearms(id: string, params?: PaginationParams): Promise<PaginatedResponse<Firearm>>;
3356
+ /**
3357
+ * Get a chronological timeline for a manufacturer.
3358
+ *
3359
+ * @param id - The manufacturer slug or ID.
3360
+ * @returns Timeline data with key events, product launches, and milestones.
3361
+ * @throws {NotFoundError} If the manufacturer does not exist.
3362
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3363
+ *
3364
+ * @example
3365
+ * ```typescript
3366
+ * const { data } = await client.manufacturers.getTimeline('browning');
3367
+ * for (const event of data.events) {
3368
+ * console.log(event.year, event.description);
3369
+ * }
3370
+ * ```
3371
+ */
3372
+ getTimeline(id: string): Promise<APIResponse<ManufacturerTimeline>>;
3373
+ /**
3374
+ * Get aggregate statistics for a manufacturer.
3375
+ *
3376
+ * @param id - The manufacturer slug or ID.
3377
+ * @returns Statistical summary including firearm counts, caliber distribution, and more.
3378
+ * @throws {NotFoundError} If the manufacturer does not exist.
3379
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3380
+ *
3381
+ * @example
3382
+ * ```typescript
3383
+ * const { data } = await client.manufacturers.getStats('glock');
3384
+ * console.log(data.totalFirearms, data.caliberBreakdown);
3385
+ * ```
3386
+ */
3387
+ getStats(id: string): Promise<APIResponse<ManufacturerStats>>;
3388
+ }
3389
+
3390
+ /**
3391
+ * Calibers resource for the GunSpec SDK.
3392
+ *
3393
+ * Provides access to all `/v1/calibers` endpoints including listing,
3394
+ * comparing, ballistics calculations, and exploring caliber families
3395
+ * and associated ammunition.
3396
+ *
3397
+ * @module
3398
+ */
3399
+
3400
+ /**
3401
+ * Resource class for interacting with the GunSpec Calibers API.
3402
+ *
3403
+ * Wraps all `/v1/calibers` endpoints. Instantiated internally by the
3404
+ * {@link GunSpec} client and exposed as `client.calibers`.
3405
+ *
3406
+ * @example
3407
+ * ```typescript
3408
+ * import GunSpec from '@gunspec/sdk';
3409
+ *
3410
+ * const client = new GunSpec();
3411
+ *
3412
+ * // List calibers
3413
+ * const { data } = await client.calibers.list({ cartridge_type: 'centerfire' });
3414
+ *
3415
+ * // Compare calibers
3416
+ * const { data: comparison } = await client.calibers.compare({
3417
+ * ids: '9x19mm-parabellum,45-acp',
3418
+ * });
3419
+ * ```
3420
+ */
3421
+ declare class CalibersResource {
3422
+ private readonly client;
3423
+ constructor(client: HttpClient);
3424
+ /**
3425
+ * List calibers with optional filters and pagination.
3426
+ *
3427
+ * @param params - Optional query parameters for filtering by cartridge type, primer type, etc.
3428
+ * @returns A paginated list of calibers.
3429
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3430
+ *
3431
+ * @example
3432
+ * ```typescript
3433
+ * const result = await client.calibers.list({
3434
+ * cartridge_type: 'centerfire',
3435
+ * primer_type: 'boxer',
3436
+ * per_page: 50,
3437
+ * });
3438
+ * ```
3439
+ */
3440
+ list(params?: ListCalibersParams): Promise<PaginatedResponse<Caliber>>;
3441
+ /**
3442
+ * Auto-paginate through all calibers matching the given filters.
3443
+ *
3444
+ * Returns an async iterator that fetches pages on demand, yielding
3445
+ * individual {@link Caliber} objects.
3446
+ *
3447
+ * @param params - Optional query parameters for filtering and sorting.
3448
+ * @returns An async iterable iterator yielding individual calibers.
3449
+ *
3450
+ * @example
3451
+ * ```typescript
3452
+ * for await (const caliber of client.calibers.listAutoPaging()) {
3453
+ * console.log(caliber.name, caliber.bulletDiameterMm);
3454
+ * }
3455
+ * ```
3456
+ */
3457
+ listAutoPaging(params?: ListCalibersParams): AsyncIterableIterator<Caliber>;
3458
+ /**
3459
+ * Compare up to 5 calibers side by side.
3460
+ *
3461
+ * @param params - Object containing comma-separated caliber IDs.
3462
+ * @returns An array of calibers with full details for comparison.
3463
+ * @throws {BadRequestError} If more than 5 IDs are provided.
3464
+ * @throws {NotFoundError} If any of the specified calibers do not exist.
3465
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3466
+ *
3467
+ * @example
3468
+ * ```typescript
3469
+ * const { data } = await client.calibers.compare({
3470
+ * ids: '9x19mm-parabellum,45-acp,40-s-w',
3471
+ * });
3472
+ * ```
3473
+ */
3474
+ compare(params: CompareCalibersParams): Promise<APIResponse<Caliber[]>>;
3475
+ /**
3476
+ * Get ballistics data for a caliber at a specified distance.
3477
+ *
3478
+ * @param params - Caliber ID and distance in meters.
3479
+ * @returns Ballistic data including velocity, energy, and drop at the specified distance.
3480
+ * @throws {NotFoundError} If the caliber does not exist.
3481
+ * @throws {BadRequestError} If the distance is out of range.
3482
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3483
+ *
3484
+ * @example
3485
+ * ```typescript
3486
+ * const { data } = await client.calibers.ballistics({
3487
+ * id: '9x19mm-parabellum',
3488
+ * distance: 100,
3489
+ * });
3490
+ * console.log(data.velocity, data.energy, data.drop);
3491
+ * ```
3492
+ */
3493
+ ballistics(params: CaliberBallisticsParams): Promise<APIResponse<BallisticsResult>>;
3494
+ /**
3495
+ * Get a single caliber by its slug or ID.
3496
+ *
3497
+ * @param id - The caliber slug (e.g. `"9x19mm-parabellum"`) or numeric ID.
3498
+ * @returns The full caliber record with all specifications.
3499
+ * @throws {NotFoundError} If no caliber matches the given identifier.
3500
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3501
+ *
3502
+ * @example
3503
+ * ```typescript
3504
+ * const { data } = await client.calibers.get('45-acp');
3505
+ * console.log(data.name, data.bulletDiameterMm, data.caseLengthMm);
3506
+ * ```
3507
+ */
3508
+ get(id: string): Promise<APIResponse<Caliber>>;
3509
+ /**
3510
+ * Get all firearms that use a specific caliber.
3511
+ *
3512
+ * @param id - The caliber slug or ID.
3513
+ * @param params - Optional pagination parameters.
3514
+ * @returns A paginated list of firearms chambered in the specified caliber.
3515
+ * @throws {NotFoundError} If the caliber does not exist.
3516
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3517
+ *
3518
+ * @example
3519
+ * ```typescript
3520
+ * const result = await client.calibers.getFirearms('9x19mm-parabellum', { per_page: 50 });
3521
+ * console.log(`${result.pagination.total} firearms use 9mm`);
3522
+ * ```
3523
+ */
3524
+ getFirearms(id: string, params?: PaginationParams): Promise<PaginatedResponse<Firearm>>;
3525
+ /**
3526
+ * Get the parent caliber chain (ancestry) for a caliber.
3527
+ *
3528
+ * @param id - The caliber slug or ID.
3529
+ * @returns An ordered array of calibers from the given caliber up to the root ancestor.
3530
+ * @throws {NotFoundError} If the caliber does not exist.
3531
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3532
+ *
3533
+ * @example
3534
+ * ```typescript
3535
+ * const { data } = await client.calibers.getParentChain('300-blackout');
3536
+ * // [300 Blackout, 5.56x45mm NATO, .223 Remington, ...]
3537
+ * ```
3538
+ */
3539
+ getParentChain(id: string): Promise<APIResponse<Caliber[]>>;
3540
+ /**
3541
+ * Get the full family tree of related calibers.
3542
+ *
3543
+ * @param id - The caliber slug or ID.
3544
+ * @returns An array of calibers in the same family (parent, siblings, children).
3545
+ * @throws {NotFoundError} If the caliber does not exist.
3546
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3547
+ *
3548
+ * @example
3549
+ * ```typescript
3550
+ * const { data } = await client.calibers.getFamily('9x19mm-parabellum');
3551
+ * console.log(data.map(c => c.name));
3552
+ * ```
3553
+ */
3554
+ getFamily(id: string): Promise<APIResponse<Caliber[]>>;
3555
+ /**
3556
+ * Get ammunition loads available for a caliber.
3557
+ *
3558
+ * @param id - The caliber slug or ID.
3559
+ * @param params - Optional pagination parameters.
3560
+ * @returns A paginated list of ammunition loads for the specified caliber.
3561
+ * @throws {NotFoundError} If the caliber does not exist.
3562
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3563
+ *
3564
+ * @example
3565
+ * ```typescript
3566
+ * const result = await client.calibers.getAmmunition('9x19mm-parabellum', { per_page: 25 });
3567
+ * for (const ammo of result.data) {
3568
+ * console.log(ammo.name, ammo.bulletWeightGrains);
3569
+ * }
3570
+ * ```
3571
+ */
3572
+ getAmmunition(id: string, params?: PaginationParams): Promise<PaginatedResponse<Ammunition>>;
3573
+ }
3574
+
3575
+ /**
3576
+ * Categories resource for the GunSpec SDK.
3577
+ *
3578
+ * Provides access to `/v1/categories` endpoints for listing firearm
3579
+ * categories and retrieving firearms within a specific category.
3580
+ *
3581
+ * @module
3582
+ */
3583
+
3584
+ /**
3585
+ * Resource class for interacting with the GunSpec Categories API.
3586
+ *
3587
+ * Wraps all `/v1/categories` endpoints. Instantiated internally by the
3588
+ * {@link GunSpec} client and exposed as `client.categories`.
3589
+ *
3590
+ * @example
3591
+ * ```typescript
3592
+ * import GunSpec from '@gunspec/sdk';
3593
+ *
3594
+ * const client = new GunSpec();
3595
+ *
3596
+ * // List all categories
3597
+ * const { data } = await client.categories.list();
3598
+ * console.log(data.map(c => c.name));
3599
+ *
3600
+ * // Get firearms in a category
3601
+ * const pistols = await client.categories.getFirearms('pistol');
3602
+ * ```
3603
+ */
3604
+ declare class CategoriesResource {
3605
+ private readonly client;
3606
+ constructor(client: HttpClient);
3607
+ /**
3608
+ * List all firearm categories.
3609
+ *
3610
+ * @returns An array of all category records.
3611
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3612
+ *
3613
+ * @example
3614
+ * ```typescript
3615
+ * const { data } = await client.categories.list();
3616
+ * // [{ slug: 'pistol', name: 'Pistol' }, { slug: 'rifle', name: 'Rifle' }, ...]
3617
+ * ```
3618
+ */
3619
+ list(): Promise<APIResponse<Category[]>>;
3620
+ /**
3621
+ * Get all firearms within a specific category.
3622
+ *
3623
+ * @param slug - The category slug (e.g. `"pistol"`, `"rifle"`, `"shotgun"`).
3624
+ * @param params - Optional pagination parameters.
3625
+ * @returns A paginated list of firearms in the specified category.
3626
+ * @throws {NotFoundError} If the category slug does not exist.
3627
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3628
+ *
3629
+ * @example
3630
+ * ```typescript
3631
+ * const result = await client.categories.getFirearms('shotgun', {
3632
+ * per_page: 25,
3633
+ * sort: 'name',
3634
+ * order: 'asc',
3635
+ * });
3636
+ * console.log(`${result.pagination.total} shotguns in database`);
3637
+ * ```
3638
+ */
3639
+ getFirearms(slug: string, params?: PaginationParams): Promise<PaginatedResponse<Firearm>>;
3640
+ }
3641
+
3642
+ /**
3643
+ * Statistics resource for the GunSpec SDK.
3644
+ *
3645
+ * Provides access to all `/v1/stats` endpoints for retrieving aggregate
3646
+ * statistics about the firearms database including production status,
3647
+ * field coverage, caliber popularity, and more.
3648
+ *
3649
+ * @module
3650
+ */
3651
+
3652
+ /**
3653
+ * Resource class for interacting with the GunSpec Statistics API.
3654
+ *
3655
+ * Wraps all `/v1/stats` endpoints. Instantiated internally by the
3656
+ * {@link GunSpec} client and exposed as `client.stats`.
3657
+ *
3658
+ * @example
3659
+ * ```typescript
3660
+ * import GunSpec from '@gunspec/sdk';
3661
+ *
3662
+ * const client = new GunSpec();
3663
+ *
3664
+ * const { data } = await client.stats.summary();
3665
+ * console.log(data.totalFirearms, data.totalManufacturers);
3666
+ * ```
3667
+ */
3668
+ declare class StatsResource {
3669
+ private readonly client;
3670
+ constructor(client: HttpClient);
3671
+ /**
3672
+ * Get a high-level summary of the database.
3673
+ *
3674
+ * @returns Summary counts for firearms, manufacturers, calibers, and categories.
3675
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3676
+ *
3677
+ * @example
3678
+ * ```typescript
3679
+ * const { data } = await client.stats.summary();
3680
+ * console.log(`Database has ${data.totalFirearms} firearms`);
3681
+ * ```
3682
+ */
3683
+ summary(): Promise<APIResponse<StatsSummary>>;
3684
+ /**
3685
+ * Get firearm counts grouped by production status.
3686
+ *
3687
+ * @returns Counts for in-production, discontinued, and prototype firearms.
3688
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3689
+ *
3690
+ * @example
3691
+ * ```typescript
3692
+ * const { data } = await client.stats.productionStatus();
3693
+ * // { in_production: 342, discontinued: 891, prototype: 12 }
3694
+ * ```
3695
+ */
3696
+ productionStatus(): Promise<APIResponse<ProductionStatusItem[]>>;
3697
+ /**
3698
+ * Get field coverage statistics across the database.
3699
+ *
3700
+ * Shows the percentage of firearms that have data for each field,
3701
+ * useful for assessing data completeness.
3702
+ *
3703
+ * @returns Per-field coverage percentages.
3704
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3705
+ *
3706
+ * @example
3707
+ * ```typescript
3708
+ * const { data } = await client.stats.fieldCoverage();
3709
+ * console.log(`Weight field: ${data.weight}% coverage`);
3710
+ * ```
3711
+ */
3712
+ fieldCoverage(): Promise<APIResponse<FieldCoverage>>;
3713
+ /**
3714
+ * Get the most popular calibers by firearm count.
3715
+ *
3716
+ * @param params - Optional limit parameter.
3717
+ * @returns An ordered list of calibers ranked by the number of firearms using them.
3718
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3719
+ *
3720
+ * @example
3721
+ * ```typescript
3722
+ * const { data } = await client.stats.popularCalibers({ limit: 10 });
3723
+ * for (const entry of data) {
3724
+ * console.log(`${entry.name}: ${entry.count} firearms`);
3725
+ * }
3726
+ * ```
3727
+ */
3728
+ popularCalibers(params?: PopularCalibersParams): Promise<APIResponse<PopularCaliber[]>>;
3729
+ /**
3730
+ * Get the most prolific manufacturers by firearm count.
3731
+ *
3732
+ * @param params - Optional limit and category filter.
3733
+ * @returns An ordered list of manufacturers ranked by the number of firearms produced.
3734
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3735
+ *
3736
+ * @example
3737
+ * ```typescript
3738
+ * const { data } = await client.stats.prolificManufacturers({
3739
+ * limit: 10,
3740
+ * category: 'pistol',
3741
+ * });
3742
+ * ```
3743
+ */
3744
+ prolificManufacturers(params?: ProlificManufacturersParams): Promise<APIResponse<ProlificManufacturer[]>>;
3745
+ /**
3746
+ * Get firearm counts grouped by category.
3747
+ *
3748
+ * @returns Counts per category (pistol, rifle, shotgun, etc.).
3749
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3750
+ *
3751
+ * @example
3752
+ * ```typescript
3753
+ * const { data } = await client.stats.byCategory();
3754
+ * // [{ category: 'pistol', count: 512 }, { category: 'rifle', count: 234 }, ...]
3755
+ * ```
3756
+ */
3757
+ byCategory(): Promise<APIResponse<CategoryStats[]>>;
3758
+ /**
3759
+ * Get firearm statistics for a specific decade/era.
3760
+ *
3761
+ * @param params - The decade string (e.g. `"1990s"`).
3762
+ * @returns Statistics for firearms introduced during the specified decade.
3763
+ * @throws {BadRequestError} If the decade format is invalid (must be like `"1990s"`).
3764
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3765
+ *
3766
+ * @example
3767
+ * ```typescript
3768
+ * const { data } = await client.stats.byEra({ decade: '1940s' });
3769
+ * ```
3770
+ */
3771
+ byEra(params: ByEraParams): Promise<APIResponse<EraStats>>;
3772
+ /**
3773
+ * Get statistics about materials used across all firearms.
3774
+ *
3775
+ * @returns Material usage counts and percentages by component type.
3776
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3777
+ *
3778
+ * @example
3779
+ * ```typescript
3780
+ * const { data } = await client.stats.materials();
3781
+ * ```
3782
+ */
3783
+ materials(): Promise<APIResponse<MaterialStats>>;
3784
+ /**
3785
+ * Get firearm adoption statistics for a specific country.
3786
+ *
3787
+ * @param params - The country code (e.g. `"US"`, `"GB"`).
3788
+ * @returns Adoption data for the specified country.
3789
+ * @throws {BadRequestError} If the country code is invalid.
3790
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3791
+ *
3792
+ * @example
3793
+ * ```typescript
3794
+ * const { data } = await client.stats.adoptionByCountry({ code: 'US' });
3795
+ * ```
3796
+ */
3797
+ adoptionByCountry(params: AdoptionByCountryParams): Promise<APIResponse<AdoptionByCountryItem[]>>;
3798
+ /**
3799
+ * Get firearm adoption statistics by usage type.
3800
+ *
3801
+ * @param params - The usage type (e.g. `"military"`, `"law_enforcement"`, `"civilian"`).
3802
+ * @returns Adoption data for the specified usage type.
3803
+ * @throws {BadRequestError} If the type is not a recognized usage category.
3804
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3805
+ *
3806
+ * @example
3807
+ * ```typescript
3808
+ * const { data } = await client.stats.adoptionByType({ type: 'military' });
3809
+ * ```
3810
+ */
3811
+ adoptionByType(params: AdoptionByTypeParams): Promise<APIResponse<AdoptionByTypeItem[]>>;
3812
+ /**
3813
+ * Get firearm counts grouped by action type.
3814
+ *
3815
+ * @param params - Optional category filter.
3816
+ * @returns Counts per action type, optionally filtered by category.
3817
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3818
+ *
3819
+ * @example
3820
+ * ```typescript
3821
+ * const { data } = await client.stats.actionTypes({ category: 'rifle' });
3822
+ * ```
3823
+ */
3824
+ actionTypes(params?: ActionTypesParams): Promise<APIResponse<ActionTypeStats[]>>;
3825
+ /**
3826
+ * Get feature frequency statistics across the database.
3827
+ *
3828
+ * @param params - Optional category filter and result limit.
3829
+ * @returns An ordered list of features ranked by frequency of occurrence.
3830
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3831
+ *
3832
+ * @example
3833
+ * ```typescript
3834
+ * const { data } = await client.stats.featureFrequency({
3835
+ * category: 'pistol',
3836
+ * limit: 20,
3837
+ * });
3838
+ * ```
3839
+ */
3840
+ featureFrequency(params?: FeatureFrequencyParams): Promise<APIResponse<FeatureFrequency[]>>;
3841
+ /**
3842
+ * Get caliber popularity trends across historical eras.
3843
+ *
3844
+ * @param params - Optional from/to decade range (e.g. `"1940s"` to `"2020s"`).
3845
+ * @returns Caliber popularity data broken down by decade.
3846
+ * @throws {BadRequestError} If the decade format is invalid.
3847
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3848
+ *
3849
+ * @example
3850
+ * ```typescript
3851
+ * const { data } = await client.stats.caliberPopularityByEra({
3852
+ * from_decade: '1940s',
3853
+ * to_decade: '2020s',
3854
+ * });
3855
+ * ```
3856
+ */
3857
+ caliberPopularityByEra(params?: CaliberPopularityByEraParams): Promise<APIResponse<CaliberPopularityByEra[]>>;
3858
+ }
3859
+
3860
+ /**
3861
+ * Game resource for the GunSpec SDK.
3862
+ *
3863
+ * Provides access to all `/v1/game` endpoints for game-balanced
3864
+ * firearm data including balance reports, tier lists, matchups,
3865
+ * role rosters, and stat distributions.
3866
+ *
3867
+ * @module
3868
+ */
3869
+
3870
+ /**
3871
+ * Resource class for interacting with the GunSpec Game API.
3872
+ *
3873
+ * Wraps all `/v1/game` endpoints. These endpoints provide game-balanced
3874
+ * firearm data suitable for game development, including normalized stats,
3875
+ * tier rankings, and balance analysis.
3876
+ *
3877
+ * Instantiated internally by the {@link GunSpec} client and exposed as
3878
+ * `client.game`.
3879
+ *
3880
+ * @example
3881
+ * ```typescript
3882
+ * import GunSpec from '@gunspec/sdk';
3883
+ *
3884
+ * const client = new GunSpec();
3885
+ *
3886
+ * const { data } = await client.game.tierList({ stat: 'damage' });
3887
+ * console.log(data.tiers);
3888
+ * ```
3889
+ */
3890
+ declare class GameResource {
3891
+ private readonly client;
3892
+ constructor(client: HttpClient);
3893
+ /**
3894
+ * Get a balance report showing outlier firearms.
3895
+ *
3896
+ * Identifies firearms whose game stats deviate significantly from
3897
+ * the average, useful for game balancing.
3898
+ *
3899
+ * @param params - Optional threshold for outlier detection (0-100).
3900
+ * @returns A balance report with overpowered and underpowered firearms.
3901
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3902
+ *
3903
+ * @example
3904
+ * ```typescript
3905
+ * const { data } = await client.game.balanceReport({ threshold: 15 });
3906
+ * console.log(data.length, 'firearms flagged as outliers');
3907
+ * ```
3908
+ */
3909
+ balanceReport(params?: BalanceReportParams): Promise<APIResponse<BalanceEntry[]>>;
3910
+ /**
3911
+ * Get a tier list of firearms ranked by a specific stat.
3912
+ *
3913
+ * @param params - Optional category filter and stat to rank by.
3914
+ * @returns A tier list with firearms grouped into S/A/B/C/D/F tiers.
3915
+ * @throws {BadRequestError} If the stat is not a valid game stat.
3916
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3917
+ *
3918
+ * @example
3919
+ * ```typescript
3920
+ * const { data } = await client.game.tierList({
3921
+ * stat: 'accuracy',
3922
+ * category: 'rifle',
3923
+ * });
3924
+ * console.log('S-tier:', data.tiers.S.map(f => f.name));
3925
+ * ```
3926
+ */
3927
+ tierList(params?: TierListParams): Promise<APIResponse<TierList>>;
3928
+ /**
3929
+ * Get a game-balanced matchup between two firearms.
3930
+ *
3931
+ * @param params - The slugs of the two firearms to compare.
3932
+ * @returns A detailed matchup result with per-stat comparisons and a winner.
3933
+ * @throws {NotFoundError} If either firearm does not exist.
3934
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3935
+ *
3936
+ * @example
3937
+ * ```typescript
3938
+ * const { data } = await client.game.matchups({ a: 'ak-47', b: 'm4-carbine' });
3939
+ * console.log(data.winner, data.statComparisons);
3940
+ * ```
3941
+ */
3942
+ matchups(params: MatchupsParams): Promise<APIResponse<MatchupResult>>;
3943
+ /**
3944
+ * Get a roster of firearms best suited for a specific game role.
3945
+ *
3946
+ * @param params - The role to query and optional count limit.
3947
+ * @returns A roster of firearms ranked by suitability for the given role.
3948
+ * @throws {BadRequestError} If the role is not a recognized game role.
3949
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3950
+ *
3951
+ * @example
3952
+ * ```typescript
3953
+ * const { data } = await client.game.roleRoster({
3954
+ * role: 'sniper',
3955
+ * count: 10,
3956
+ * });
3957
+ * for (const firearm of data) {
3958
+ * console.log(firearm.name, firearm.roleScore);
3959
+ * }
3960
+ * ```
3961
+ */
3962
+ roleRoster(params: RoleRosterParams): Promise<APIResponse<RoleRosterItem[]>>;
3963
+ /**
3964
+ * Get the statistical distribution for a specific game stat.
3965
+ *
3966
+ * Shows how firearms are distributed across value ranges for a given
3967
+ * stat, useful for understanding the spread and identifying balance issues.
3968
+ *
3969
+ * @param params - The stat to analyze (e.g. `"damage"`, `"accuracy"`).
3970
+ * @returns Distribution data including histogram buckets and summary statistics.
3971
+ * @throws {BadRequestError} If the stat is not a valid game stat.
3972
+ * @throws {AuthenticationError} If the API key is missing or invalid.
3973
+ *
3974
+ * @example
3975
+ * ```typescript
3976
+ * const { data } = await client.game.statDistribution({ stat: 'damage' });
3977
+ * console.log(data.mean, data.median, data.buckets);
3978
+ * ```
3979
+ */
3980
+ statDistribution(params: StatDistributionParams): Promise<APIResponse<StatDistribution>>;
3981
+ }
3982
+
3983
+ /**
3984
+ * Game Stats Snapshots resource for the GunSpec SDK.
3985
+ *
3986
+ * Provides access to `/v1/game-stats` endpoints for retrieving versioned
3987
+ * snapshots of game-balanced firearm statistics. Each snapshot captures
3988
+ * the game stats at a specific point in time, enabling version tracking
3989
+ * and historical comparisons.
3990
+ *
3991
+ * @module
3992
+ */
3993
+
3994
+ /**
3995
+ * Resource class for interacting with the GunSpec Game Stats Snapshots API.
3996
+ *
3997
+ * Wraps all `/v1/game-stats` endpoints. Instantiated internally by the
3998
+ * {@link GunSpec} client and exposed as `client.gameStats`.
3999
+ *
4000
+ * @example
4001
+ * ```typescript
4002
+ * import GunSpec from '@gunspec/sdk';
4003
+ *
4004
+ * const client = new GunSpec();
4005
+ *
4006
+ * // List all snapshot versions
4007
+ * const { data: versions } = await client.gameStats.listVersions();
4008
+ *
4009
+ * // Get firearms from a specific version
4010
+ * const result = await client.gameStats.listFirearms('1.0.0');
4011
+ * ```
4012
+ */
4013
+ declare class GameStatsResource {
4014
+ private readonly client;
4015
+ constructor(client: HttpClient);
4016
+ /**
4017
+ * List all available game stats snapshot versions.
4018
+ *
4019
+ * @returns An array of version records with metadata about each snapshot.
4020
+ * @throws {AuthenticationError} If the API key is missing or invalid.
4021
+ *
4022
+ * @example
4023
+ * ```typescript
4024
+ * const { data } = await client.gameStats.listVersions();
4025
+ * for (const version of data) {
4026
+ * console.log(version.version, version.createdAt, version.description);
4027
+ * }
4028
+ * ```
4029
+ */
4030
+ listVersions(): Promise<APIResponse<GameStatsVersion[]>>;
4031
+ /**
4032
+ * List all firearms in a specific game stats snapshot version.
4033
+ *
4034
+ * @param version - The snapshot version string (e.g. `"1.0.0"`).
4035
+ * @param params - Optional pagination parameters.
4036
+ * @returns A paginated list of firearms with their game stats for the given version.
4037
+ * @throws {NotFoundError} If the specified version does not exist.
4038
+ * @throws {AuthenticationError} If the API key is missing or invalid.
4039
+ *
4040
+ * @example
4041
+ * ```typescript
4042
+ * const result = await client.gameStats.listFirearms('1.0.0', { per_page: 50 });
4043
+ * for (const firearm of result.data) {
4044
+ * console.log(firearm.name, firearm.damage);
4045
+ * }
4046
+ * ```
4047
+ */
4048
+ listFirearms(version: string, params?: ListSnapshotFirearmsParams): Promise<PaginatedResponse<GameStatsSnapshotEntry>>;
4049
+ /**
4050
+ * Get a single firearm's game stats from a specific snapshot version.
4051
+ *
4052
+ * @param version - The snapshot version string (e.g. `"1.0.0"`).
4053
+ * @param id - The firearm slug or ID.
4054
+ * @returns The firearm's game stats as captured in the specified version.
4055
+ * @throws {NotFoundError} If the version or firearm does not exist in the snapshot.
4056
+ * @throws {AuthenticationError} If the API key is missing or invalid.
4057
+ *
4058
+ * @example
4059
+ * ```typescript
4060
+ * const { data } = await client.gameStats.getFirearm('1.0.0', 'glock-g17');
4061
+ * console.log(data.damage, data.accuracy, data.range);
4062
+ * ```
4063
+ */
4064
+ getFirearm(version: string, id: string): Promise<APIResponse<GameStatsSnapshotEntry>>;
4065
+ }
4066
+
4067
+ /**
4068
+ * Ammunition resource for the GunSpec SDK.
4069
+ *
4070
+ * Provides access to all `/v1/ammunition` endpoints including listing,
4071
+ * retrieving details, bullet SVG generation, and ballistic calculations.
4072
+ *
4073
+ * @module
4074
+ */
4075
+
4076
+ /**
4077
+ * Resource class for interacting with the GunSpec Ammunition API.
4078
+ *
4079
+ * Wraps all `/v1/ammunition` endpoints. Instantiated internally by the
4080
+ * {@link GunSpec} client and exposed as `client.ammunition`.
4081
+ *
4082
+ * @example
4083
+ * ```typescript
4084
+ * import GunSpec from '@gunspec/sdk';
4085
+ *
4086
+ * const client = new GunSpec();
4087
+ *
4088
+ * // List ammunition
4089
+ * const { data } = await client.ammunition.list({ caliber_id: '9x19mm-parabellum' });
4090
+ *
4091
+ * // Get ballistics for a specific load
4092
+ * const { data: ballistics } = await client.ammunition.ballistics(
4093
+ * '9mm-federal-hst-124gr',
4094
+ * { distances: '0,25,50,100' },
4095
+ * );
4096
+ * ```
4097
+ */
4098
+ declare class AmmunitionResource {
4099
+ private readonly client;
4100
+ constructor(client: HttpClient);
4101
+ /**
4102
+ * List ammunition with optional filters and pagination.
4103
+ *
4104
+ * @param params - Optional query parameters for filtering by caliber, bullet type, etc.
4105
+ * @returns A paginated list of ammunition loads.
4106
+ * @throws {AuthenticationError} If the API key is missing or invalid.
4107
+ *
4108
+ * @example
4109
+ * ```typescript
4110
+ * const result = await client.ammunition.list({
4111
+ * caliber_id: '9x19mm-parabellum',
4112
+ * bullet_type: 'hollow-point',
4113
+ * per_page: 25,
4114
+ * });
4115
+ * ```
4116
+ */
4117
+ list(params?: ListAmmunitionParams): Promise<PaginatedResponse<Ammunition>>;
4118
+ /**
4119
+ * Auto-paginate through all ammunition matching the given filters.
4120
+ *
4121
+ * Returns an async iterator that fetches pages on demand, yielding
4122
+ * individual {@link Ammunition} objects.
4123
+ *
4124
+ * @param params - Optional query parameters for filtering and sorting.
4125
+ * @returns An async iterable iterator yielding individual ammunition loads.
4126
+ *
4127
+ * @example
4128
+ * ```typescript
4129
+ * for await (const ammo of client.ammunition.listAutoPaging({ caliber_id: '45-acp' })) {
4130
+ * console.log(ammo.name, ammo.bulletWeightGrains);
4131
+ * }
4132
+ * ```
4133
+ */
4134
+ listAutoPaging(params?: ListAmmunitionParams): AsyncIterableIterator<Ammunition>;
4135
+ /**
4136
+ * Get a single ammunition load by its slug or ID.
4137
+ *
4138
+ * @param id - The ammunition slug (e.g. `"9mm-federal-hst-124gr"`) or numeric ID.
4139
+ * @returns The full ammunition record with specifications.
4140
+ * @throws {NotFoundError} If no ammunition matches the given identifier.
4141
+ * @throws {AuthenticationError} If the API key is missing or invalid.
4142
+ *
4143
+ * @example
4144
+ * ```typescript
4145
+ * const { data } = await client.ammunition.get('9mm-federal-hst-124gr');
4146
+ * console.log(data.name, data.muzzleVelocityFps, data.bulletWeightGrains);
4147
+ * ```
4148
+ */
4149
+ get(id: string): Promise<APIResponse<Ammunition>>;
4150
+ /**
4151
+ * Get a bullet profile SVG image for an ammunition load.
4152
+ *
4153
+ * Returns a raw SVG string representing the bullet projectile shape.
4154
+ * This endpoint returns raw SVG content, not the standard JSON envelope.
4155
+ *
4156
+ * @param id - The ammunition slug or ID.
4157
+ * @returns The raw SVG string of the bullet profile.
4158
+ * @throws {NotFoundError} If the ammunition does not exist.
4159
+ *
4160
+ * @example
4161
+ * ```typescript
4162
+ * const svg = await client.ammunition.getBulletSvg('9mm-federal-hst-124gr');
4163
+ * // Insert into DOM: element.innerHTML = svg;
4164
+ * ```
4165
+ */
4166
+ getBulletSvg(id: string): Promise<string>;
4167
+ /**
4168
+ * Get ballistic trajectory data for an ammunition load.
4169
+ *
4170
+ * @param id - The ammunition slug or ID.
4171
+ * @param params - Optional barrel length and distance parameters.
4172
+ * @returns Ballistic trajectory data at specified distances.
4173
+ * @throws {NotFoundError} If the ammunition does not exist.
4174
+ * @throws {AuthenticationError} If the API key is missing or invalid.
4175
+ *
4176
+ * @example
4177
+ * ```typescript
4178
+ * const { data } = await client.ammunition.ballistics('9mm-federal-hst-124gr', {
4179
+ * barrel_length_mm: 102,
4180
+ * distances: '0,25,50,100,200',
4181
+ * });
4182
+ * for (const point of data.trajectory) {
4183
+ * console.log(`${point.distanceM}m: ${point.velocityFps} fps, ${point.energyFtLbs} ft-lbs`);
4184
+ * }
4185
+ * ```
4186
+ */
4187
+ ballistics(id: string, params?: AmmunitionBallisticsParams): Promise<APIResponse<BallisticProfile>>;
4188
+ }
4189
+
4190
+ /**
4191
+ * Countries resource for the GunSpec SDK.
4192
+ *
4193
+ * Provides access to `/v1/countries` endpoints for listing countries
4194
+ * and retrieving a country's full firearm arsenal.
4195
+ *
4196
+ * @module
4197
+ */
4198
+
4199
+ /**
4200
+ * Resource class for interacting with the GunSpec Countries API.
4201
+ *
4202
+ * Wraps all `/v1/countries` endpoints. Instantiated internally by the
4203
+ * {@link GunSpec} client and exposed as `client.countries`.
4204
+ *
4205
+ * @example
4206
+ * ```typescript
4207
+ * import GunSpec from '@gunspec/sdk';
4208
+ *
4209
+ * const client = new GunSpec();
4210
+ *
4211
+ * // List all countries
4212
+ * const { data } = await client.countries.list();
4213
+ *
4214
+ * // Get a country's full arsenal
4215
+ * const { data: arsenal } = await client.countries.getArsenal('US');
4216
+ * ```
4217
+ */
4218
+ declare class CountriesResource {
4219
+ private readonly client;
4220
+ constructor(client: HttpClient);
4221
+ /**
4222
+ * List all countries that have adopted at least one firearm.
4223
+ *
4224
+ * @returns An array of country records with codes and names.
4225
+ * @throws {AuthenticationError} If the API key is missing or invalid.
4226
+ *
4227
+ * @example
4228
+ * ```typescript
4229
+ * const { data } = await client.countries.list();
4230
+ * for (const country of data) {
4231
+ * console.log(country.code, country.name);
4232
+ * }
4233
+ * ```
4234
+ */
4235
+ list(): Promise<APIResponse<Country[]>>;
4236
+ /**
4237
+ * Get the full firearm arsenal for a specific country.
4238
+ *
4239
+ * Returns all firearms adopted by the country, grouped by usage type
4240
+ * (military, law enforcement, etc.).
4241
+ *
4242
+ * @param code - The country code (e.g. `"US"`, `"GB"`, `"DE"`).
4243
+ * @returns The country's arsenal data with firearms grouped by adoption type.
4244
+ * @throws {NotFoundError} If the country code is not found in the database.
4245
+ * @throws {AuthenticationError} If the API key is missing or invalid.
4246
+ *
4247
+ * @example
4248
+ * ```typescript
4249
+ * const { data } = await client.countries.getArsenal('US');
4250
+ * console.log(`${data.country.name} has ${data.totalFirearms} firearms`);
4251
+ * for (const group of data.groups) {
4252
+ * console.log(`${group.type}: ${group.firearms.length} firearms`);
4253
+ * }
4254
+ * ```
4255
+ */
4256
+ getArsenal(code: string): Promise<APIResponse<CountryArsenal>>;
4257
+ }
4258
+
4259
+ /**
4260
+ * Conflicts resource for the GunSpec SDK.
4261
+ *
4262
+ * Provides access to the `/v1/conflicts` endpoint for listing military
4263
+ * conflicts and their associated firearms.
4264
+ *
4265
+ * @module
4266
+ */
4267
+
4268
+ /**
4269
+ * Resource class for interacting with the GunSpec Conflicts API.
4270
+ *
4271
+ * Wraps the `/v1/conflicts` endpoint. Instantiated internally by the
4272
+ * {@link GunSpec} client and exposed as `client.conflicts`.
4273
+ *
4274
+ * @example
4275
+ * ```typescript
4276
+ * import GunSpec from '@gunspec/sdk';
4277
+ *
4278
+ * const client = new GunSpec();
4279
+ *
4280
+ * const { data } = await client.conflicts.list();
4281
+ * for (const conflict of data) {
4282
+ * console.log(conflict.name, conflict.startYear, conflict.endYear);
4283
+ * }
4284
+ * ```
4285
+ */
4286
+ declare class ConflictsResource {
4287
+ private readonly client;
4288
+ constructor(client: HttpClient);
4289
+ /**
4290
+ * List all military conflicts in the database.
4291
+ *
4292
+ * Returns conflicts with metadata including name, date range, and
4293
+ * participating nations. Use the conflict identifiers with
4294
+ * `client.firearms.byConflict()` to find firearms used in a specific conflict.
4295
+ *
4296
+ * @returns An array of all conflict records.
4297
+ * @throws {AuthenticationError} If the API key is missing or invalid.
4298
+ *
4299
+ * @example
4300
+ * ```typescript
4301
+ * const { data } = await client.conflicts.list();
4302
+ * for (const conflict of data) {
4303
+ * console.log(`${conflict.name} (${conflict.startYear}-${conflict.endYear})`);
4304
+ * }
4305
+ *
4306
+ * // Then find firearms used in a conflict:
4307
+ * const wwii = await client.firearms.byConflict({ conflict: 'world-war-ii' });
4308
+ * ```
4309
+ */
4310
+ list(): Promise<APIResponse<Conflict[]>>;
4311
+ }
4312
+
4313
+ /**
4314
+ * Data Quality resource for the GunSpec SDK.
4315
+ *
4316
+ * Provides access to `/v1/data` endpoints for assessing database
4317
+ * coverage and per-record confidence scores.
4318
+ *
4319
+ * @module
4320
+ */
4321
+
4322
+ /**
4323
+ * Resource class for interacting with the GunSpec Data Quality API.
4324
+ *
4325
+ * Wraps all `/v1/data` endpoints. Instantiated internally by the
4326
+ * {@link GunSpec} client and exposed as `client.dataQuality`.
4327
+ *
4328
+ * @example
4329
+ * ```typescript
4330
+ * import GunSpec from '@gunspec/sdk';
4331
+ *
4332
+ * const client = new GunSpec();
4333
+ *
4334
+ * // Check overall data coverage
4335
+ * const { data } = await client.dataQuality.coverage();
4336
+ * console.log(`${data.overallCoverage}% overall field coverage`);
4337
+ *
4338
+ * // Find low-confidence records
4339
+ * const lowConf = await client.dataQuality.confidence({ below: 0.3 });
4340
+ * ```
4341
+ */
4342
+ declare class DataQualityResource {
4343
+ private readonly client;
4344
+ constructor(client: HttpClient);
4345
+ /**
4346
+ * Get overall data coverage statistics for the database.
4347
+ *
4348
+ * Returns per-field and aggregate coverage metrics showing what
4349
+ * percentage of records have data for each field.
4350
+ *
4351
+ * @returns Data coverage statistics including per-field percentages.
4352
+ * @throws {AuthenticationError} If the API key is missing or invalid.
4353
+ *
4354
+ * @example
4355
+ * ```typescript
4356
+ * const { data } = await client.dataQuality.coverage();
4357
+ * console.log(`Overall: ${data.overallCoverage}%`);
4358
+ * for (const field of data.fields) {
4359
+ * console.log(`${field.name}: ${field.coverage}%`);
4360
+ * }
4361
+ * ```
4362
+ */
4363
+ coverage(): Promise<APIResponse<DataCoverage>>;
4364
+ /**
4365
+ * Get firearms with confidence scores below a specified threshold.
4366
+ *
4367
+ * Useful for identifying records that may need additional data
4368
+ * verification or enrichment.
4369
+ *
4370
+ * @param params - Optional threshold and pagination parameters.
4371
+ * @returns A paginated list of firearms with their confidence scores.
4372
+ * @throws {BadRequestError} If the threshold is outside the 0-1 range.
4373
+ * @throws {AuthenticationError} If the API key is missing or invalid.
4374
+ *
4375
+ * @example
4376
+ * ```typescript
4377
+ * const result = await client.dataQuality.confidence({
4378
+ * below: 0.5,
4379
+ * per_page: 25,
4380
+ * });
4381
+ * for (const item of result.data) {
4382
+ * console.log(`${item.name}: confidence ${item.confidenceScore}`);
4383
+ * }
4384
+ * ```
4385
+ */
4386
+ confidence(params?: ConfidenceParams): Promise<PaginatedResponse<ConfidenceEntry>>;
4387
+ }
4388
+
4389
+ declare class FavoritesResource {
4390
+ private readonly client;
4391
+ constructor(client: HttpClient);
4392
+ list(params?: ListFavoritesParams): Promise<PaginatedResponse<Favorite>>;
4393
+ add(firearmId: string): Promise<APIResponse<FavoriteToggle>>;
4394
+ remove(firearmId: string): Promise<APIResponse<FavoriteToggle>>;
4395
+ }
4396
+
4397
+ declare class ReportsResource {
4398
+ private readonly client;
4399
+ constructor(client: HttpClient);
4400
+ create(params: CreateReportParams): Promise<APIResponse<DataReport>>;
4401
+ list(params?: ListReportsParams): Promise<PaginatedResponse<DataReport>>;
4402
+ }
4403
+
4404
+ declare class SupportResource {
4405
+ private readonly client;
4406
+ constructor(client: HttpClient);
4407
+ create(params: CreateTicketParams): Promise<APIResponse<SupportTicket>>;
4408
+ list(params?: ListTicketsParams): Promise<PaginatedResponse<SupportTicket>>;
4409
+ get(ticketId: string): Promise<APIResponse<SupportTicketDetail>>;
4410
+ reply(ticketId: string, params: CreateReplyParams): Promise<APIResponse<SupportTicketReply>>;
4411
+ }
4412
+
4413
+ declare class WebhooksResource {
4414
+ private readonly client;
4415
+ constructor(client: HttpClient);
4416
+ list(params?: ListWebhookEndpointsParams): Promise<PaginatedResponse<WebhookEndpoint>>;
4417
+ create(params: CreateWebhookEndpointParams): Promise<APIResponse<WebhookEndpoint>>;
4418
+ get(id: string): Promise<APIResponse<WebhookEndpoint>>;
4419
+ update(id: string, params: UpdateWebhookEndpointParams): Promise<APIResponse<WebhookEndpoint>>;
4420
+ delete(id: string): Promise<APIResponse<{
4421
+ deleted: boolean;
4422
+ }>>;
4423
+ test(id: string): Promise<APIResponse<WebhookTestResult>>;
4424
+ }
4425
+
4426
+ declare class UsageResource {
4427
+ private readonly client;
4428
+ constructor(client: HttpClient);
4429
+ get(params?: UsageParams): Promise<APIResponse<UsageStats>>;
4430
+ }
4431
+
4432
+ /**
4433
+ * Configuration options for the GunSpec client.
4434
+ *
4435
+ * @example
4436
+ * ```typescript
4437
+ * // Reads GUNSPEC_API_KEY from process.env automatically
4438
+ * const client = new GunSpec();
4439
+ *
4440
+ * // Or pass via environment variable explicitly
4441
+ * const client = new GunSpec({
4442
+ * apiKey: process.env.GUNSPEC_API_KEY,
4443
+ * });
4444
+ * ```
4445
+ */
4446
+ interface ClientOptions {
4447
+ /**
4448
+ * API key for authentication. If not provided, reads from
4449
+ * the `GUNSPEC_API_KEY` environment variable.
4450
+ */
4451
+ apiKey?: string;
4452
+ /**
4453
+ * Base URL for the API. Defaults to `https://api.gunspec.io`.
4454
+ */
4455
+ baseURL?: string;
4456
+ /**
4457
+ * Request timeout in milliseconds. Defaults to 30000 (30s).
4458
+ */
4459
+ timeout?: number;
4460
+ /**
4461
+ * Retry configuration for failed requests.
4462
+ */
4463
+ retry?: RetryConfig;
4464
+ /**
4465
+ * Custom default headers to include with every request.
4466
+ */
4467
+ defaultHeaders?: Record<string, string>;
4468
+ }
4469
+ /**
4470
+ * GunSpec.io API client — the main entry point for the SDK.
4471
+ *
4472
+ * Provides typed access to the firearms specification database API
4473
+ * through resource-oriented properties that mirror the API structure.
4474
+ *
4475
+ * @example
4476
+ * ```typescript
4477
+ * import { GunSpec } from '@gunspec/sdk';
4478
+ *
4479
+ * // Auto-reads GUNSPEC_API_KEY from environment
4480
+ * const client = new GunSpec();
4481
+ *
4482
+ * // List firearms with filters
4483
+ * const { data, pagination } = await client.firearms.list({
4484
+ * category: 'pistol',
4485
+ * status: 'in_production',
4486
+ * per_page: 50,
4487
+ * });
4488
+ *
4489
+ * // Get a specific firearm by slug
4490
+ * const { data: glock } = await client.firearms.get('glock-g17');
4491
+ *
4492
+ * // Search firearms
4493
+ * const results = await client.firearms.search({ q: 'beretta 92' });
4494
+ *
4495
+ * // Compare firearms side by side
4496
+ * const { data: comparison } = await client.firearms.compare({
4497
+ * ids: 'glock-g17,beretta-92fs,sig-sauer-p226',
4498
+ * });
4499
+ *
4500
+ * // Auto-paginate through all results
4501
+ * for await (const firearm of client.firearms.listAutoPaging({ category: 'rifle' })) {
4502
+ * console.log(firearm.name);
4503
+ * }
4504
+ *
4505
+ * // Game development features
4506
+ * const { data: tierList } = await client.game.tierList({ stat: 'damage' });
4507
+ * const { data: matchup } = await client.game.matchups({ a: 'ak-47', b: 'm16a4' });
4508
+ *
4509
+ * // Statistics
4510
+ * const { data: summary } = await client.stats.summary();
4511
+ * ```
4512
+ */
4513
+ declare class GunSpec {
4514
+ /** Firearms specifications, search, comparison, and related data */
4515
+ readonly firearms: FirearmsResource;
4516
+ /** Firearm manufacturers and their products */
4517
+ readonly manufacturers: ManufacturersResource;
4518
+ /** Ammunition calibers and cartridge specifications */
4519
+ readonly calibers: CalibersResource;
4520
+ /** Firearm categories (pistol, rifle, shotgun, etc.) */
4521
+ readonly categories: CategoriesResource;
4522
+ /** Aggregate statistics across the database */
4523
+ readonly stats: StatsResource;
4524
+ /** Game development tools — balance reports, tier lists, matchups */
4525
+ readonly game: GameResource;
4526
+ /** Versioned game stats snapshots for pinning game builds */
4527
+ readonly gameStats: GameStatsResource;
4528
+ /** Ammunition loads, ballistics, and bullet data */
4529
+ readonly ammunition: AmmunitionResource;
4530
+ /** Countries and their military/law enforcement arsenals */
4531
+ readonly countries: CountriesResource;
4532
+ /** Armed conflicts and the firearms used in them */
4533
+ readonly conflicts: ConflictsResource;
4534
+ /** Data quality metrics (Enterprise tier) */
4535
+ readonly dataQuality: DataQualityResource;
4536
+ /** User's favorited firearms */
4537
+ readonly favorites: FavoritesResource;
4538
+ /** Data quality reports */
4539
+ readonly reports: ReportsResource;
4540
+ /** Support tickets (paid tiers) */
4541
+ readonly support: SupportResource;
4542
+ /** Webhook endpoint management (studio+ tier) */
4543
+ readonly webhooks: WebhooksResource;
4544
+ /** API usage statistics */
4545
+ readonly usage: UsageResource;
4546
+ /** The underlying HTTP client (for advanced use) */
4547
+ private readonly _client;
4548
+ constructor(options?: ClientOptions);
4549
+ }
4550
+
4551
+ /** Current SDK version */
4552
+ declare const VERSION = "0.1.0";
4553
+
4554
+ export { APIError, type APIErrorResponse, type APIResponse, type ActionTypesParams, type AdoptionByCountryParams, type AdoptionByTypeParams, type AdoptionMap, type Ammunition, type AmmunitionBallisticsParams, AmmunitionResource, type AuthConfig, AuthenticationError, BadRequestError, type BalanceReport, type BalanceReportParams, type BallisticsResult, type ByActionParams, type ByConflictParams, type ByDesignerParams, type ByEraParams, type ByFeatureParams, type ByMaterialParams, type CalculateBallisticsParams, type Caliber, type CaliberBallisticsParams, type CaliberPopularityByEraParams, CalibersResource, CategoriesResource, type Category, type ClientOptions, type CompareCalibersParams, type CompareFirearmsParams, type ConfidenceParams, type Conflict, ConflictsResource, ConnectionError, CountriesResource, type Country, type CountryArsenal, type CreateReplyParams, type CreateReportParams, type CreateTicketParams, type CreateWebhookEndpointParams, type DataCoverage, DataQualityResource, type DataReport, type Dimensions, type ErrorBody, type FamilyTree, type Favorite, type FavoriteToggle, FavoritesResource, type FeatureFrequencyParams, type FilterOptions, type Firearm, type FirearmImage, type FirearmUser, FirearmsResource, type GameMetaParams, type GameProfile, GameResource, type GameStats, GameStatsResource, type GameStatsVersion, GunSpec, GunSpecError, type HeadToHead, type HeadToHeadParams, HttpClient, type HttpClientConfig, InternalServerError, type ListAmmunitionParams, type ListCalibersParams, type ListFavoritesParams, type ListFirearmsParams, type ListManufacturersParams, type ListReportsParams, type ListSnapshotFirearmsParams, type ListTicketsParams, type ListWebhookEndpointsParams, type LoadFirearmParams, type Manufacturer, type ManufacturerStats, type ManufacturerTimeline, ManufacturersResource, type MatchupResult, type MatchupsParams, NotFoundError, Page, type PageFetcher, type PaginatedResponse, type PaginationMeta, type PaginationParams, PermissionError, type PopularCalibersParams, type PowerRating, type PowerRatingParams, type ProlificManufacturersParams, type RandomFirearmParams, RateLimitError, type RateLimitInfo, ReportsResource, type RequestConfig, type ResolvedRetryConfig, type RetryConfig, type RoleRoster, type RoleRosterItem, type RoleRosterParams, type SearchFirearmsParams, type Silhouette, type SilhouetteParams, type SimilarFirearm, type StatDistribution, type StatDistributionParams, StatsResource, type StatsSummary, SupportResource, type SupportTicket, type SupportTicketDetail, type SupportTicketReply, type TierList, type TierListParams, type TimelineParams, TimeoutError, type TopFirearmsParams, type UpdateWebhookEndpointParams, type UsageParams, UsageResource, type UsageStats, VERSION, type WebhookEndpoint, type WebhookTestResult, WebhooksResource, buildAuthHeaders, createAPIError, createPage, resolveApiKey };