hantaviruswrapper 1.0.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.
package/README.md ADDED
@@ -0,0 +1,159 @@
1
+ # hantaviruswrapper
2
+
3
+ A TypeScript wrapper for the [hantaOSINT](https://hantaosint.com) API, an open-source intelligence feed that tracks hantavirus outbreaks worldwide.
4
+
5
+ This wrapper only covers the **free tier**. Right now, the only free route is:
6
+
7
+ ```text
8
+ GET /api/v1/public.json
9
+ ```
10
+
11
+ > `public.json` is refreshed **once per day** by hantaOSINT. Fetching it more often just returns the same data, so you should cache it (see [Caching](#caching)).
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install hantaviruswrapper
17
+ ```
18
+
19
+ ## Quick start
20
+
21
+ ```ts
22
+ import {
23
+ getPublicResponseInfo,
24
+ getCountriesByStatus,
25
+ getOutbreaksByStrain,
26
+ } from "hantaviruswrapper";
27
+
28
+ const data = await getPublicResponseInfo();
29
+
30
+ const activeCountries = getCountriesByStatus(data, "active");
31
+ const hanSeoulOutbreaks = getOutbreaksByStrain(data, "Seoul");
32
+ ```
33
+
34
+ ## API
35
+
36
+ ### Fetching data
37
+
38
+ #### `getPublicResponseInfo()`
39
+
40
+ Fetches `GET /api/v1/public.json` and returns a typed `PublicJsonResponse`:
41
+
42
+ | Field | Type | Description |
43
+ | --- | --- | --- |
44
+ | `meta` | `Meta` | Tier, license, generation time |
45
+ | `stats` | `Stats` | Global case/death counts |
46
+ | `countries` | `Country[]` | Per-country breakdown |
47
+ | `map_markers` | `MapMarker[]` | Geolocated case markers |
48
+ | `briefs` | `Brief[]` | Recent situation updates |
49
+ | `outbreaks` | `Outbreak[]` | Named outbreak summaries |
50
+
51
+ ### Country filters
52
+
53
+ Every filter takes a `PublicJsonResponse` as its first argument. Fetch once, filter as many times as you want.
54
+
55
+ | Function | Signature | Description |
56
+ | --- | --- | --- |
57
+ | `getCountryByCode` | `(data, code: string) => Country \| undefined` | Exact match by country code (case-insensitive) |
58
+ | `getCountryByIso` | `(data, iso: string) => Country \| undefined` | Exact match by ISO alpha-3 (case-insensitive) |
59
+ | `getCountriesByStatus` | `(data, status: CountryStatus) => Country[]` | `"active"`, `"suspected"`, `"lockdown"`, `"historical"` |
60
+ | `getCountriesByTrend` | `(data, trend: Trend) => Country[]` | `"up"`, `"down"`, `"flat"` |
61
+ | `searchCountriesByName` | `(data, name: string) => Country[]` | Partial name search (case-insensitive) |
62
+
63
+ ### Outbreak filters
64
+
65
+ | Function | Signature | Description |
66
+ | --- | --- | --- |
67
+ | `getOutbreakBySlug` | `(data, slug: string) => Outbreak \| undefined` | Exact match by slug |
68
+ | `getOutbreaksByStatus` | `(data, status: OutbreakStatus) => Outbreak[]` | `"active"`, `"resolved"` |
69
+ | `getOutbreaksByStrain` | `(data, strain: string) => Outbreak[]` | Partial strain search (case-insensitive) |
70
+ | `searchOutbreaksByName` | `(data, name: string) => Outbreak[]` | Partial name search (case-insensitive) |
71
+ | `getOutbreaksByOrigin` | `(data, origin: string) => Outbreak[]` | Partial origin search (case-insensitive) |
72
+
73
+ ## Caching
74
+
75
+ Since `public.json` only updates daily, you really don't need to fetch it every time. `createCachedFetcher` gives you a simple in-memory cache with a TTL you set:
76
+
77
+ ```ts
78
+ import { createCachedFetcher, getCountriesByStatus } from "hantaviruswrapper";
79
+
80
+ // cache for 1 hour (the data only changes daily anyway)
81
+ const fetchData = createCachedFetcher(3_600_000);
82
+
83
+ const data = await fetchData(); // hits the API
84
+ const data2 = await fetchData(); // returns cached, no request
85
+
86
+ const active = getCountriesByStatus(data2, "active");
87
+ ```
88
+
89
+ Default TTL is 5 minutes. You can force a refresh whenever:
90
+
91
+ ```ts
92
+ fetchData.invalidate();
93
+ const fresh = await fetchData(); // hits the API again
94
+ ```
95
+
96
+ You're also free to manage your own cache. Every filter function just takes a `PublicJsonResponse`, so pass in whatever you have.
97
+
98
+ ## Rate limiting
99
+
100
+ The free tier has these limits:
101
+
102
+ | Tier | Limit | Burst | On exceed |
103
+ | --- | --- | --- | --- |
104
+ | Free | 60 / minute | 120 | HTTP 429 |
105
+
106
+ The wrapper includes a client-side token-bucket rate limiter that stops you from sending requests that would get rejected. It's built into the client, so you don't need to set anything up.
107
+
108
+ If a request would go over the limit, a `RateLimitError` is thrown before the request goes out:
109
+
110
+ ```ts
111
+ import { RateLimitError, rateLimiter } from "hantaviruswrapper";
112
+
113
+ // check first
114
+ if (rateLimiter.canRequest()) {
115
+ const data = await getPublicResponseInfo();
116
+ }
117
+
118
+ // or catch it
119
+ try {
120
+ const data = await getPublicResponseInfo();
121
+ } catch (e) {
122
+ if (e instanceof RateLimitError) {
123
+ console.log(`Retry after ${e.retryAfterMs}ms`);
124
+ }
125
+ }
126
+ ```
127
+
128
+ If you're caching (and you should be), you'll probably never hit this.
129
+
130
+ ## Types
131
+
132
+ All types are exported:
133
+
134
+ ```ts
135
+ import type {
136
+ PublicJsonResponse,
137
+ Meta,
138
+ Stats,
139
+ Country,
140
+ CountryStatus,
141
+ Trend,
142
+ MapMarker,
143
+ MarkerCategory,
144
+ Brief,
145
+ BriefTag,
146
+ Outbreak,
147
+ OutbreakStatus,
148
+ } from "hantaviruswrapper";
149
+ ```
150
+
151
+ ## About hantaOSINT
152
+
153
+ [hantaOSINT](https://hantaosint.com) tracks hantavirus activity globally. The API has free and paid tiers. This wrapper only covers the free routes.
154
+
155
+ Right now the only active free route is `GET /api/v1/public.json`. If more free endpoints get added, this wrapper will be updated to support them.
156
+
157
+ ## License
158
+
159
+ ISC
package/dist/cache.js ADDED
@@ -0,0 +1,20 @@
1
+ import { getPublicResponseInfo } from "./service/getPublicJsonData.js";
2
+ export const createCachedFetcher = (ttlMs = 300_000) => {
3
+ let cached = null;
4
+ let cachedAt = 0;
5
+ const fetcher = async () => {
6
+ const now = Date.now();
7
+ if (cached && (now - cachedAt) < ttlMs) {
8
+ return cached;
9
+ }
10
+ cached = await getPublicResponseInfo();
11
+ cachedAt = Date.now();
12
+ return cached;
13
+ };
14
+ fetcher.invalidate = () => {
15
+ cached = null;
16
+ cachedAt = 0;
17
+ };
18
+ return fetcher;
19
+ };
20
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAQvE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,QAAgB,OAAO,EAAiB,EAAE;IAC7E,IAAI,MAAM,GAA8B,IAAI,CAAC;IAC7C,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,MAAM,OAAO,GAAG,KAAK,IAAiC,EAAE;QACvD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,MAAM,IAAI,CAAC,GAAG,GAAG,QAAQ,CAAC,GAAG,KAAK,EAAE,CAAC;YACxC,OAAO,MAAM,CAAC;QACf,CAAC;QACD,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QACvC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACtB,OAAO,MAAM,CAAC;IACf,CAAC,CAAC;IAEF,OAAO,CAAC,UAAU,GAAG,GAAG,EAAE;QACzB,MAAM,GAAG,IAAI,CAAC;QACd,QAAQ,GAAG,CAAC,CAAC;IACd,CAAC,CAAC;IAEF,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,34 @@
1
+ import { GenericRequestError } from "./types/GenericRequestError.js";
2
+ import { rateLimiter } from "./rateLimit.js";
3
+ export const BASE_URL = "https://hantaosint.com/api/v1";
4
+ // the shape of this client will change in the future, i literally copied this from another project lol
5
+ export const hantaVirusClient = async (endpoint, path, requestType = 'GET') => {
6
+ rateLimiter.consume();
7
+ const requestUrl = new URL(`${BASE_URL}${endpoint}`);
8
+ // naming this request body since it is confusing occasionally
9
+ let reqBody;
10
+ if (path)
11
+ requestUrl.pathname += `/${path}`;
12
+ try {
13
+ const response = await fetch(requestUrl.toString(), {
14
+ method: requestType,
15
+ headers: {
16
+ 'Accept': 'application/json'
17
+ },
18
+ body: reqBody
19
+ });
20
+ // the api does not return error result (or if it does, it isn't documented) and it honestly does not matter for now since we are not doing any searching
21
+ const data = await response.json();
22
+ if (!response.ok || (data && typeof data === "object" && "error" in data)) {
23
+ throw new Error(`hantaosint error: ${response.status}`);
24
+ }
25
+ return data;
26
+ }
27
+ catch (error) {
28
+ if (error instanceof Error) {
29
+ throw new GenericRequestError(endpoint, undefined, error.message);
30
+ }
31
+ throw new GenericRequestError(endpoint);
32
+ }
33
+ };
34
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,MAAM,CAAC,MAAM,QAAQ,GAAG,+BAA+B,CAAC;AAExD,uGAAuG;AACvG,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EAAK,QAAgB,EAAE,IAAa,EAAE,cAAsB,KAAK,EAAc,EAAE;IACrH,WAAW,CAAC,OAAO,EAAE,CAAC;IAEtB,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,GAAG,QAAQ,GAAG,QAAQ,EAAE,CAAC,CAAC;IAErD,8DAA8D;IAC9D,IAAI,OAAoC,CAAC;IAEzC,IAAI,IAAI;QAAE,UAAU,CAAC,QAAQ,IAAI,IAAI,IAAI,EAAE,CAAC;IAE5C,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE;YACnD,MAAM,EAAE,WAAW;YACnB,OAAO,EAAE;gBACR,QAAQ,EAAE,kBAAkB;aAC5B;YACD,IAAI,EAAE,OAAO;SACb,CAAC,CAAC;QAEH,yJAAyJ;QACzJ,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAO,CAAC;QAExC,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC;YAC3E,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACzD,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACzB,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC5B,MAAM,IAAI,mBAAmB,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,IAAI,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;AACF,CAAC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { getPublicResponseInfo } from "./service/getPublicJsonData.js";
2
+ export { getCountryByCode, getCountryByIso, getCountriesByStatus, getCountriesByTrend, searchCountriesByName } from "./service/filterCountries.js";
3
+ export { getOutbreakBySlug, getOutbreaksByStatus, getOutbreaksByStrain, searchOutbreaksByName, getOutbreaksByOrigin } from "./service/filterOutbreaks.js";
4
+ export { createCachedFetcher } from "./cache.js";
5
+ export { RateLimiter, RateLimitError, rateLimiter } from "./rateLimit.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACnJ,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAC1J,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,45 @@
1
+ export class RateLimitError extends Error {
2
+ retryAfterMs;
3
+ constructor(retryAfterMs) {
4
+ super(`Rate limit exceeded. Retry after ${Math.ceil(retryAfterMs)}ms`);
5
+ this.name = "RateLimitError";
6
+ this.retryAfterMs = Math.ceil(retryAfterMs);
7
+ }
8
+ }
9
+ export class RateLimiter {
10
+ tokens;
11
+ lastRefill;
12
+ maxTokens;
13
+ refillRatePerMs;
14
+ constructor(maxPerMinute = 60, burst = 120) {
15
+ this.maxTokens = burst;
16
+ this.tokens = burst;
17
+ this.refillRatePerMs = maxPerMinute / 60_000;
18
+ this.lastRefill = Date.now();
19
+ }
20
+ refill() {
21
+ const now = Date.now();
22
+ const elapsed = now - this.lastRefill;
23
+ this.tokens = Math.min(this.maxTokens, this.tokens + elapsed * this.refillRatePerMs);
24
+ this.lastRefill = now;
25
+ }
26
+ canRequest() {
27
+ this.refill();
28
+ return this.tokens >= 1;
29
+ }
30
+ get retryAfterMs() {
31
+ this.refill();
32
+ if (this.tokens >= 1)
33
+ return 0;
34
+ return Math.ceil((1 - this.tokens) / this.refillRatePerMs);
35
+ }
36
+ consume() {
37
+ this.refill();
38
+ if (this.tokens < 1) {
39
+ throw new RateLimitError(Math.ceil((1 - this.tokens) / this.refillRatePerMs));
40
+ }
41
+ this.tokens -= 1;
42
+ }
43
+ }
44
+ export const rateLimiter = new RateLimiter();
45
+ //# sourceMappingURL=rateLimit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rateLimit.js","sourceRoot":"","sources":["../src/rateLimit.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,cAAe,SAAQ,KAAK;IACxB,YAAY,CAAS;IAErC,YAAY,YAAoB;QAC/B,KAAK,CAAC,oCAAoC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACvE,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC7C,CAAC;CACD;AAED,MAAM,OAAO,WAAW;IACf,MAAM,CAAS;IACf,UAAU,CAAS;IACV,SAAS,CAAS;IAClB,eAAe,CAAS;IAEzC,YAAY,eAAuB,EAAE,EAAE,QAAgB,GAAG;QACzD,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,eAAe,GAAG,YAAY,GAAG,MAAM,CAAC;QAC7C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC9B,CAAC;IAEO,MAAM;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC;QACtC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC;QACrF,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC;IACvB,CAAC;IAED,UAAU;QACT,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,OAAO,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IACzB,CAAC;IAED,IAAI,YAAY;QACf,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO,CAAC,CAAC;QAC/B,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO;QACN,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;QAC/E,CAAC;QACD,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IAClB,CAAC;CACD;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC"}
@@ -0,0 +1,17 @@
1
+ export const getCountryByCode = (data, code) => {
2
+ return data.countries.find(c => c.code.toLowerCase() === code.toLowerCase());
3
+ };
4
+ export const getCountryByIso = (data, iso) => {
5
+ return data.countries.find(c => c.iso_a3.toLowerCase() === iso.toLowerCase());
6
+ };
7
+ export const getCountriesByStatus = (data, status) => {
8
+ return data.countries.filter(c => c.status === status);
9
+ };
10
+ export const getCountriesByTrend = (data, trend) => {
11
+ return data.countries.filter(c => c.trend === trend);
12
+ };
13
+ export const searchCountriesByName = (data, name) => {
14
+ const lower = name.toLowerCase();
15
+ return data.countries.filter(c => c.name.toLowerCase().includes(lower));
16
+ };
17
+ //# sourceMappingURL=filterCountries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filterCountries.js","sourceRoot":"","sources":["../../src/service/filterCountries.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAwB,EAAE,IAAY,EAAuB,EAAE;IAC/F,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;AAC9E,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,IAAwB,EAAE,GAAW,EAAuB,EAAE;IAC7F,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;AAC/E,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,IAAwB,EAAE,MAAqB,EAAa,EAAE;IAClG,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;AACxD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,IAAwB,EAAE,KAAY,EAAa,EAAE;IACxF,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;AACtD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,IAAwB,EAAE,IAAY,EAAa,EAAE;IAC1F,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AACzE,CAAC,CAAC"}
@@ -0,0 +1,19 @@
1
+ export const getOutbreakBySlug = (data, slug) => {
2
+ return data.outbreaks.find(o => o.slug === slug);
3
+ };
4
+ export const getOutbreaksByStatus = (data, status) => {
5
+ return data.outbreaks.filter(o => o.status === status);
6
+ };
7
+ export const getOutbreaksByStrain = (data, strain) => {
8
+ const lower = strain.toLowerCase();
9
+ return data.outbreaks.filter(o => o.strain.toLowerCase().includes(lower));
10
+ };
11
+ export const searchOutbreaksByName = (data, name) => {
12
+ const lower = name.toLowerCase();
13
+ return data.outbreaks.filter(o => o.name.toLowerCase().includes(lower));
14
+ };
15
+ export const getOutbreaksByOrigin = (data, origin) => {
16
+ const lower = origin.toLowerCase();
17
+ return data.outbreaks.filter(o => o.origin.toLowerCase().includes(lower));
18
+ };
19
+ //# sourceMappingURL=filterOutbreaks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filterOutbreaks.js","sourceRoot":"","sources":["../../src/service/filterOutbreaks.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,IAAwB,EAAE,IAAY,EAAwB,EAAE;IACjG,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAClD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,IAAwB,EAAE,MAAsB,EAAc,EAAE;IACpG,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;AACxD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,IAAwB,EAAE,MAAc,EAAc,EAAE;IAC5F,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACnC,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AAC3E,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,IAAwB,EAAE,IAAY,EAAc,EAAE;IAC3F,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AACzE,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,IAAwB,EAAE,MAAc,EAAc,EAAE;IAC5F,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACnC,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AAC3E,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { hantaVirusClient } from "../client.js";
2
+ export const getPublicResponseInfo = async () => {
3
+ return await hantaVirusClient("/public.json");
4
+ };
5
+ //# sourceMappingURL=getPublicJsonData.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getPublicJsonData.js","sourceRoot":"","sources":["../../src/service/getPublicJsonData.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,IAAI,EAAE;IAC/C,OAAO,MAAM,gBAAgB,CAAqB,cAAc,CAAC,CAAC;AACnE,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=BaseResponse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseResponse.js","sourceRoot":"","sources":["../../src/types/BaseResponse.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ export class GenericRequestError extends Error {
2
+ constructor(endpoint, status, message) {
3
+ super(`AUR API error on ${endpoint}: ${status ? `HTTP ${status}` : message}`);
4
+ this.name = 'AURError';
5
+ }
6
+ }
7
+ //# sourceMappingURL=GenericRequestError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GenericRequestError.js","sourceRoot":"","sources":["../../src/types/GenericRequestError.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC7C,YAAY,QAAgB,EAAE,MAAe,EAAE,OAAgB;QAC9D,KAAK,CAAC,oBAAoB,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,MAAM,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9E,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACxB,CAAC;CACD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=publicJsonResponse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publicJsonResponse.js","sourceRoot":"","sources":["../../src/types/publicJsonResponse.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "hantaviruswrapper",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "types": "./dist/index.d.ts",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "default": "./dist/index.js"
10
+ }
11
+ },
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "prepublishOnly": "npm run build",
15
+ "test": "NODE_OPTIONS='--experimental-vm-modules' jest --runInBand"
16
+ },
17
+ "keywords": [],
18
+ "author": "",
19
+ "license": "ISC",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/grMLEqomlkkU5Eeinz4brIrOVCUCkJuN/hantaviruswrapper"
23
+ },
24
+ "type": "module",
25
+ "devDependencies": {
26
+ "@tsconfig/node22": "^22.0.5",
27
+ "@types/jest": "^30.0.0",
28
+ "@types/node": "^25.6.0",
29
+ "@types/supertest": "^7.2.0",
30
+ "eslint": "^10.2.1",
31
+ "jest": "^30.2.0",
32
+ "nodemon": "^3.1.11",
33
+ "ts-jest": "^29.4.6",
34
+ "tsx": "^4.21.0",
35
+ "typescript": "^6.0.3",
36
+ "typescript-eslint": "^8.51.0"
37
+ },
38
+ "jest": {
39
+ "preset": "ts-jest/presets/default-esm",
40
+ "testEnvironment": "node",
41
+ "roots": [
42
+ "<rootDir>/tests"
43
+ ],
44
+ "testMatch": [
45
+ "**/*.test.ts"
46
+ ],
47
+ "collectCoverageFrom": [
48
+ "lib/**/*.{ts,js}",
49
+ "!lib/**/*.d.ts"
50
+ ],
51
+ "transform": {
52
+ "^.+\\.ts$": [
53
+ "ts-jest",
54
+ {
55
+ "useESM": true
56
+ }
57
+ ]
58
+ },
59
+ "moduleNameMapper": {
60
+ "^(\\.{1,2}/.*)\\.js$": "$1"
61
+ },
62
+ "moduleFileExtensions": [
63
+ "ts",
64
+ "js",
65
+ "json",
66
+ "node"
67
+ ],
68
+ "extensionsToTreatAsEsm": [
69
+ ".ts"
70
+ ]
71
+ }
72
+ }