iqair-api 1.0.1
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 +104 -0
- package/dist/index.d.mts +284 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +206 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# iqair-api
|
|
2
|
+
|
|
3
|
+
Fully typed TypeScript SDK for scraping IQAir air quality data.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add iqair-api
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { createIQAirClient } from "iqair-api";
|
|
15
|
+
|
|
16
|
+
const client = createIQAirClient();
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
const dataSource = "central-pollution-control-board";
|
|
20
|
+
|
|
21
|
+
const stations = await client.getStations({ dataSource });
|
|
22
|
+
console.log(`Found ${stations.length} stations`);
|
|
23
|
+
|
|
24
|
+
const nearest = await client.getNearestStation({
|
|
25
|
+
lat: 28.6139,
|
|
26
|
+
lng: 77.209,
|
|
27
|
+
dataSource,
|
|
28
|
+
});
|
|
29
|
+
console.log("Nearest station:", nearest);
|
|
30
|
+
|
|
31
|
+
if (nearest) {
|
|
32
|
+
const aqi = await client.getStationAQI({ stationUrl: nearest.url });
|
|
33
|
+
console.log("Station AQI:", aqi);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const locationAQI = await client.getAQIForLocation({
|
|
37
|
+
lat: 28.6139,
|
|
38
|
+
lng: 77.209,
|
|
39
|
+
dataSource,
|
|
40
|
+
});
|
|
41
|
+
console.log("Location AQI:", locationAQI);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
main().catch(console.error);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## API
|
|
48
|
+
|
|
49
|
+
### `createIQAirClient(config?)`
|
|
50
|
+
|
|
51
|
+
| Option | Type | Required | Description |
|
|
52
|
+
|--------|------|----------|-------------|
|
|
53
|
+
| `baseUrl` | `string` | No | IQAir base URL (default: `https://www.iqair.com`) |
|
|
54
|
+
| `locale` | `string` | No | Locale path segment (default: `in-en`) |
|
|
55
|
+
| `userAgent` | `string` | No | Custom user agent string |
|
|
56
|
+
|
|
57
|
+
### Methods
|
|
58
|
+
|
|
59
|
+
| Method | Description |
|
|
60
|
+
|--------|-------------|
|
|
61
|
+
| `getStations(params)` | Get all stations for a data source |
|
|
62
|
+
| `getStationAQI(params)` | Get AQI data for a specific station |
|
|
63
|
+
| `getNearestStation(params)` | Find the nearest station to coordinates |
|
|
64
|
+
| `getNearestStations(params)` | Find nearest stations to coordinates (sorted) |
|
|
65
|
+
| `getAQIForLocation(params)` | Get AQI for the nearest station to coordinates |
|
|
66
|
+
|
|
67
|
+
### Types
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import type {
|
|
71
|
+
Station,
|
|
72
|
+
StationWithDistance,
|
|
73
|
+
StationDetails,
|
|
74
|
+
StationItem,
|
|
75
|
+
AQIResponse,
|
|
76
|
+
CurrentConditions,
|
|
77
|
+
Pollutant,
|
|
78
|
+
HistoricalDataPoint,
|
|
79
|
+
ForecastItem,
|
|
80
|
+
GetStationsParams,
|
|
81
|
+
GetStationAQIParams,
|
|
82
|
+
GetNearestStationParams,
|
|
83
|
+
GetNearestStationsParams,
|
|
84
|
+
GetAQIForLocationParams,
|
|
85
|
+
} from "iqair-api";
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Development
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
bun install
|
|
92
|
+
bun run build
|
|
93
|
+
bun test
|
|
94
|
+
bun test:unit
|
|
95
|
+
bun test:e2e
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## License
|
|
99
|
+
|
|
100
|
+
MIT
|
|
101
|
+
|
|
102
|
+
## Disclaimer
|
|
103
|
+
|
|
104
|
+
This is an **unofficial** API client and is not affiliated with, endorsed by, or associated with IQAir or its parent organization. This package is provided for educational and informational purposes under fair use. Accessing publicly available air quality data is lawful and serves the public interest.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
//#region src/client/iqair-client.config.d.ts
|
|
2
|
+
interface IQAirClientConfig {
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
locale?: string;
|
|
5
|
+
userAgent?: string;
|
|
6
|
+
}
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/types/station/station.interface.d.ts
|
|
9
|
+
interface Station {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
url: string;
|
|
13
|
+
latitude: number;
|
|
14
|
+
longitude: number;
|
|
15
|
+
city?: string;
|
|
16
|
+
country?: string;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/types/station/station-with-distance.interface.d.ts
|
|
20
|
+
interface StationWithDistance extends Station {
|
|
21
|
+
distance: number;
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/types/station/station-item.interface.d.ts
|
|
25
|
+
interface StationItem {
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
url: string;
|
|
29
|
+
coordinates: {
|
|
30
|
+
latitude: number;
|
|
31
|
+
longitude: number;
|
|
32
|
+
};
|
|
33
|
+
current: {
|
|
34
|
+
aqi: {
|
|
35
|
+
value: number;
|
|
36
|
+
color: string;
|
|
37
|
+
label: string;
|
|
38
|
+
};
|
|
39
|
+
ts: string;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/types/aqi/pollutant.interface.d.ts
|
|
44
|
+
interface Pollutant {
|
|
45
|
+
unit: string;
|
|
46
|
+
description: string;
|
|
47
|
+
aqi: number;
|
|
48
|
+
concentration: number;
|
|
49
|
+
pollutantName: string;
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/types/aqi/current-conditions.interface.d.ts
|
|
53
|
+
interface CurrentConditions {
|
|
54
|
+
ts: string;
|
|
55
|
+
aqi: number;
|
|
56
|
+
mainPollutant: string;
|
|
57
|
+
concentration: number;
|
|
58
|
+
estimated: boolean;
|
|
59
|
+
condition?: string;
|
|
60
|
+
icon?: string;
|
|
61
|
+
humidity?: number;
|
|
62
|
+
pressure?: number;
|
|
63
|
+
temperature?: number;
|
|
64
|
+
wind?: {
|
|
65
|
+
speed: number;
|
|
66
|
+
direction: number;
|
|
67
|
+
};
|
|
68
|
+
pollutants?: Pollutant[];
|
|
69
|
+
aqiDescription?: string;
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/types/forecast/forecast-item.interface.d.ts
|
|
73
|
+
interface ForecastItem {
|
|
74
|
+
ts: string;
|
|
75
|
+
aqi?: number;
|
|
76
|
+
pressure?: number;
|
|
77
|
+
humidity?: number;
|
|
78
|
+
wind?: {
|
|
79
|
+
speed: number;
|
|
80
|
+
direction: number;
|
|
81
|
+
};
|
|
82
|
+
icon?: string;
|
|
83
|
+
condition?: string;
|
|
84
|
+
temperature?: number | {
|
|
85
|
+
max: number;
|
|
86
|
+
min: number;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/types/station/station-details.interface.d.ts
|
|
91
|
+
interface StationDetails {
|
|
92
|
+
id: string;
|
|
93
|
+
name: string;
|
|
94
|
+
country: string;
|
|
95
|
+
state: string;
|
|
96
|
+
type: string;
|
|
97
|
+
timezone: string;
|
|
98
|
+
coordinates: {
|
|
99
|
+
latitude: number;
|
|
100
|
+
longitude: number;
|
|
101
|
+
};
|
|
102
|
+
current: CurrentConditions;
|
|
103
|
+
forecasts?: {
|
|
104
|
+
hourly: ForecastItem[];
|
|
105
|
+
daily: ForecastItem[];
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/types/history/historical-data-point.interface.d.ts
|
|
110
|
+
interface HistoricalDataPoint {
|
|
111
|
+
ts: string;
|
|
112
|
+
aqi?: number;
|
|
113
|
+
pm25?: {
|
|
114
|
+
aqi: number;
|
|
115
|
+
concentration: number;
|
|
116
|
+
};
|
|
117
|
+
pm10?: {
|
|
118
|
+
aqi: number;
|
|
119
|
+
concentration: number;
|
|
120
|
+
};
|
|
121
|
+
o3?: {
|
|
122
|
+
aqi: number;
|
|
123
|
+
concentration: number;
|
|
124
|
+
};
|
|
125
|
+
no2?: {
|
|
126
|
+
aqi: number;
|
|
127
|
+
concentration: number;
|
|
128
|
+
};
|
|
129
|
+
so2?: {
|
|
130
|
+
aqi: number;
|
|
131
|
+
concentration: number;
|
|
132
|
+
};
|
|
133
|
+
co?: {
|
|
134
|
+
aqi: number;
|
|
135
|
+
concentration: number;
|
|
136
|
+
};
|
|
137
|
+
pressure?: number;
|
|
138
|
+
humidity?: number;
|
|
139
|
+
temperature?: number | {
|
|
140
|
+
max: number;
|
|
141
|
+
min: number;
|
|
142
|
+
};
|
|
143
|
+
wind?: {
|
|
144
|
+
speed: number;
|
|
145
|
+
direction: number;
|
|
146
|
+
};
|
|
147
|
+
condition?: string;
|
|
148
|
+
icon?: string;
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region src/types/aqi/aqi-response.interface.d.ts
|
|
152
|
+
interface AQIResponse {
|
|
153
|
+
station: {
|
|
154
|
+
id: string;
|
|
155
|
+
name: string;
|
|
156
|
+
url: string;
|
|
157
|
+
distance: number;
|
|
158
|
+
};
|
|
159
|
+
current: {
|
|
160
|
+
ts: string;
|
|
161
|
+
aqi: number;
|
|
162
|
+
mainPollutant?: string;
|
|
163
|
+
concentration?: number;
|
|
164
|
+
condition?: string;
|
|
165
|
+
temperature?: number;
|
|
166
|
+
humidity?: number;
|
|
167
|
+
pressure?: number;
|
|
168
|
+
wind?: {
|
|
169
|
+
speed: number;
|
|
170
|
+
direction: number;
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
pollutants?: Array<{
|
|
174
|
+
name: string;
|
|
175
|
+
aqi: number;
|
|
176
|
+
concentration: number;
|
|
177
|
+
unit: string;
|
|
178
|
+
}>;
|
|
179
|
+
historical?: {
|
|
180
|
+
hourly: HistoricalDataPoint[];
|
|
181
|
+
daily: HistoricalDataPoint[];
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/types/params/get-stations.params.d.ts
|
|
186
|
+
interface GetStationsParams {
|
|
187
|
+
dataSource: string;
|
|
188
|
+
limit?: number;
|
|
189
|
+
}
|
|
190
|
+
//#endregion
|
|
191
|
+
//#region src/types/params/get-station-aqi.params.d.ts
|
|
192
|
+
interface GetStationAQIParams {
|
|
193
|
+
stationUrl: string;
|
|
194
|
+
}
|
|
195
|
+
//#endregion
|
|
196
|
+
//#region src/types/params/get-nearest-station.params.d.ts
|
|
197
|
+
interface GetNearestStationParams {
|
|
198
|
+
lat: number;
|
|
199
|
+
lng: number;
|
|
200
|
+
dataSource: string;
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/types/params/get-nearest-stations.params.d.ts
|
|
204
|
+
interface GetNearestStationsParams {
|
|
205
|
+
lat: number;
|
|
206
|
+
lng: number;
|
|
207
|
+
dataSource: string;
|
|
208
|
+
limit?: number;
|
|
209
|
+
}
|
|
210
|
+
//#endregion
|
|
211
|
+
//#region src/types/params/get-aqi-for-location.params.d.ts
|
|
212
|
+
interface GetAQIForLocationParams {
|
|
213
|
+
lat: number;
|
|
214
|
+
lng: number;
|
|
215
|
+
dataSource: string;
|
|
216
|
+
}
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region src/types/internal/station-list-response.interface.d.ts
|
|
219
|
+
interface StationListData {
|
|
220
|
+
profileStations?: {
|
|
221
|
+
data: StationItem[];
|
|
222
|
+
response: Record<string, unknown>;
|
|
223
|
+
};
|
|
224
|
+
stationsMapData?: {
|
|
225
|
+
data: StationItem[];
|
|
226
|
+
response: Record<string, unknown>;
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
interface StationListResponse {
|
|
230
|
+
"routes/$(locale)": {
|
|
231
|
+
data: StationListData;
|
|
232
|
+
};
|
|
233
|
+
"routes/$(locale).profile.$slug": {
|
|
234
|
+
data: StationListData;
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region src/types/internal/individual-station-response.interface.d.ts
|
|
239
|
+
interface IndividualStationData {
|
|
240
|
+
details?: StationDetails;
|
|
241
|
+
measurements?: {
|
|
242
|
+
data?: {
|
|
243
|
+
measurements?: {
|
|
244
|
+
hourly?: HistoricalDataPoint[];
|
|
245
|
+
daily?: HistoricalDataPoint[];
|
|
246
|
+
};
|
|
247
|
+
};
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
interface IndividualStationResponse {
|
|
251
|
+
root: {
|
|
252
|
+
data: IndividualStationData;
|
|
253
|
+
};
|
|
254
|
+
"routes/$": {
|
|
255
|
+
data: IndividualStationData;
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region src/client/iqair-client.d.ts
|
|
260
|
+
declare class IQAirClient {
|
|
261
|
+
private readonly baseUrl;
|
|
262
|
+
private readonly locale;
|
|
263
|
+
private readonly userAgent;
|
|
264
|
+
constructor(config?: IQAirClientConfig);
|
|
265
|
+
private request;
|
|
266
|
+
getStations(params: GetStationsParams): Promise<Station[]>;
|
|
267
|
+
getStationAQI(params: GetStationAQIParams): Promise<AQIResponse>;
|
|
268
|
+
getNearestStation(params: GetNearestStationParams): Promise<StationWithDistance | null>;
|
|
269
|
+
getNearestStations(params: GetNearestStationsParams): Promise<StationWithDistance[]>;
|
|
270
|
+
getAQIForLocation(params: GetAQIForLocationParams): Promise<AQIResponse | null>;
|
|
271
|
+
}
|
|
272
|
+
//#endregion
|
|
273
|
+
//#region src/client/create-iqair-client.d.ts
|
|
274
|
+
declare function createIQAirClient(config?: IQAirClientConfig): IQAirClient;
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/exceptions/iqair.exception.d.ts
|
|
277
|
+
declare class IQAirException extends Error {
|
|
278
|
+
readonly statusCode: number;
|
|
279
|
+
readonly body?: string;
|
|
280
|
+
constructor(message: string, statusCode: number, body?: string);
|
|
281
|
+
}
|
|
282
|
+
//#endregion
|
|
283
|
+
export { AQIResponse, CurrentConditions, ForecastItem, GetAQIForLocationParams, GetNearestStationParams, GetNearestStationsParams, GetStationAQIParams, GetStationsParams, HistoricalDataPoint, IQAirClient, type IQAirClientConfig, IQAirException, IndividualStationData, IndividualStationResponse, Pollutant, Station, StationDetails, StationItem, StationListData, StationListResponse, StationWithDistance, createIQAirClient };
|
|
284
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/client/iqair-client.config.ts","../src/types/station/station.interface.ts","../src/types/station/station-with-distance.interface.ts","../src/types/station/station-item.interface.ts","../src/types/aqi/pollutant.interface.ts","../src/types/aqi/current-conditions.interface.ts","../src/types/forecast/forecast-item.interface.ts","../src/types/station/station-details.interface.ts","../src/types/history/historical-data-point.interface.ts","../src/types/aqi/aqi-response.interface.ts","../src/types/params/get-stations.params.ts","../src/types/params/get-station-aqi.params.ts","../src/types/params/get-nearest-station.params.ts","../src/types/params/get-nearest-stations.params.ts","../src/types/params/get-aqi-for-location.params.ts","../src/types/internal/station-list-response.interface.ts","../src/types/internal/individual-station-response.interface.ts","../src/client/iqair-client.ts","../src/client/create-iqair-client.ts","../src/exceptions/iqair.exception.ts"],"sourcesContent":[],"mappings":";UAAiB,iBAAA;EAAA,OAAA,CAAA,EAAA,MAAA;;;;;;UCAA,OAAA;EDAA,EAAA,EAAA,MAAA;;;;ECAA,SAAA,EAAO,MAAA;;;;;;ADAP,UEEA,mBAAA,SAA4B,OFFX,CAAA;;;;;UGAjB,WAAA;EHAA,EAAA,EAAA,MAAA;;;;ICAA,QAAO,EAAA,MAAA;;;;ICEP,GAAA,EAAA;;;;ICFA,CAAA;;;;;;UCAA,SAAA;EJAA,IAAA,EAAA,MAAA;;;;ECAA,aAAO,EAAA,MAAA;;;;ADAP,UKEA,iBAAA,CLFiB;;;;ECAjB,aAAO,EAAA,MAAA;;;;ECEP,QAAA,CAAA,EAAA,MAAA;;;;ICFA,KAAA,EAAA,MAAW;;;eEiBb;EDjBE,cAAS,CAAA,EAAA,MAAA;;;;UEAT,YAAA;ENAA,EAAA,EAAA,MAAA;;;;ECAA,IAAA,CAAA,EAAA;;;;ECEA,IAAA,CAAA,EAAA,MAAA;;;;ICFA,GAAA,EAAA,MAAW;;;;;UIGX,cAAA;;;ENHA,OAAA,EAAA,MAAO;;;;ECEP,WAAA,EAAA;;;;ECFA,OAAA,EIcN,iBJdiB;;YIgBhB;WACD;EHjBM,CAAA;;;;UIAA,mBAAA;ERAA,EAAA,EAAA,MAAA;;;;ICAA,aAAO,EAAA,MAAA;;;;ICEP,aAAA,EAAA,MAAoB;;;;ICFpB,aAAW,EAAA,MAAA;;;;ICAX,aAAS,EAAA,MAAA;;;;ICET,aAAA,EAAA,MAAiB;;;;ICFjB,aAAY,EAAA,MAAA;;;;ECGZ,WAAA,CAAA,EAAA,MAAc,GAAA;IAWpB,GAAA,EAAA,MAAA;IAEC,GAAA,EAAA,MAAA;EACD,CAAA;EAAY,IAAA,CAAA,EAAA;;;;ECjBN,SAAA,CAAA,EAAA,MAAA;;;;;ARAA,USEA,WAAA,CTFiB;;;;ICAjB,GAAA,EAAA,MAAO;;;;ICEP,EAAA,EAAA,MAAA;;;;ICFA,SAAA,CAAW,EAAA,MAAA;;;;ICAX,IAAA,CAAA,EAAA;;;;ECEA,CAAA;eIqBF;;;IHvBE,aAAY,EAAA,MAAA;;;;ICGZ,MAAA,EE2BL,mBF3BmB,EAAA;IAWpB,KAAA,EEiBA,mBFjBA,EAAA;EAEC,CAAA;;;;UGhBK,iBAAA;EVAA,UAAA,EAAA,MAAA;;;;;UWAA,mBAAA;EXAA,UAAA,EAAA,MAAA;;;;UYAA,uBAAA;EZAA,GAAA,EAAA,MAAA;;;;;;UaAA,wBAAA;EbAA,GAAA,EAAA,MAAA;;;;ACAjB;;;UaAiB,uBAAA;EdAA,GAAA,EAAA,MAAA;;;;;;AAAA,UeEA,eAAA,CfFiB;;UeIxB;cACI;EdLG,CAAA;;UcQP;cACI;EbPG,CAAA;;UaWA,mBAAA;;IZbA,IAAA,EYeP,eZfkB;;;UYkBlB;EXlBO,CAAA;;;;UYGA,qBAAA;YACL;;IfJK,IAAA,CAAA,EAAO;;iBeQP;gBACD;MdPC,CAAA;;;;ACFA,UaeA,yBAAA,CbfW;;UaiBlB;;EZjBO,UAAA,EAAS;UYoBhB;;;;;cCKG,WAAA;;;EhBzBI,iBAAO,SAAA;uBgB8BF;;sBAUM,oBAAoB,QAAQ;EftCvC,aAAA,CAAA,MAAA,EeiEa,mBfjEsB,CAAA,EeiEA,OfjEA,CeiEQ,WfjER,CAAA;4BeuHxC,0BACP,QAAQ;6BAMD,2BACP,QAAQ;4BAMD,0BACP,QAAQ;AdxIb;;;iBeGgB,iBAAA,UAA2B,oBAAoB;;;cCHlD,cAAA,SAAuB,KAAA;EnBAnB,SAAA,UAAA,EAAiB,MAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { UNSAFE_decodeViaTurboStream } from "react-router";
|
|
2
|
+
|
|
3
|
+
//#region src/constants/base-url.constant.ts
|
|
4
|
+
const DEFAULT_BASE_URL = "https://www.iqair.com";
|
|
5
|
+
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/constants/locale.constant.ts
|
|
8
|
+
const DEFAULT_LOCALE = "in-en";
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/constants/routes.constant.ts
|
|
12
|
+
const STATION_LIST_ROUTES = encodeURIComponent("routes/$(locale),routes/$(locale).profile.$slug");
|
|
13
|
+
const INDIVIDUAL_STATION_ROUTES = encodeURIComponent("routes/$");
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/constants/user-agent.constant.ts
|
|
17
|
+
const DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36";
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/exceptions/iqair.exception.ts
|
|
21
|
+
var IQAirException = class extends Error {
|
|
22
|
+
statusCode;
|
|
23
|
+
body;
|
|
24
|
+
constructor(message, statusCode, body) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "IQAirException";
|
|
27
|
+
this.statusCode = statusCode;
|
|
28
|
+
this.body = body;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/utils/decoder.util.ts
|
|
34
|
+
async function resolveAllPromises(obj) {
|
|
35
|
+
if (obj instanceof Promise) return resolveAllPromises(await obj);
|
|
36
|
+
if (Array.isArray(obj)) return await Promise.all(obj.map(resolveAllPromises));
|
|
37
|
+
if (obj !== null && typeof obj === "object") {
|
|
38
|
+
const entries = Object.entries(obj);
|
|
39
|
+
const resolvedEntries = await Promise.all(entries.map(async ([key, value]) => [key, await resolveAllPromises(value)]));
|
|
40
|
+
return Object.fromEntries(resolvedEntries);
|
|
41
|
+
}
|
|
42
|
+
return obj;
|
|
43
|
+
}
|
|
44
|
+
async function decodeTurboStream(stream) {
|
|
45
|
+
const decoded = await UNSAFE_decodeViaTurboStream(stream, globalThis);
|
|
46
|
+
await decoded.done;
|
|
47
|
+
return await resolveAllPromises(decoded.value);
|
|
48
|
+
}
|
|
49
|
+
async function fetchAndDecode(url, userAgent) {
|
|
50
|
+
const response = await fetch(url, { headers: {
|
|
51
|
+
"User-Agent": userAgent,
|
|
52
|
+
Accept: "*/*",
|
|
53
|
+
"Accept-Language": "en-US,en;q=0.9"
|
|
54
|
+
} });
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
const text = await response.text();
|
|
57
|
+
throw new IQAirException(`Request failed: ${response.status} ${response.statusText}`, response.status, text);
|
|
58
|
+
}
|
|
59
|
+
if (!response.body) throw new IQAirException("Response body is null", 500);
|
|
60
|
+
return decodeTurboStream(response.body);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/utils/geo.util.ts
|
|
65
|
+
const EARTH_RADIUS_KM = 6371;
|
|
66
|
+
function toRadians(degrees) {
|
|
67
|
+
return degrees * (Math.PI / 180);
|
|
68
|
+
}
|
|
69
|
+
function haversineDistance(lat1, lon1, lat2, lon2) {
|
|
70
|
+
const dLat = toRadians(lat2 - lat1);
|
|
71
|
+
const dLon = toRadians(lon2 - lon1);
|
|
72
|
+
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
|
73
|
+
return EARTH_RADIUS_KM * (2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)));
|
|
74
|
+
}
|
|
75
|
+
function findNearestStation(latitude, longitude, stations) {
|
|
76
|
+
if (stations.length === 0) return null;
|
|
77
|
+
let nearest = null;
|
|
78
|
+
let minDistance = Infinity;
|
|
79
|
+
for (const station of stations) {
|
|
80
|
+
const distance = haversineDistance(latitude, longitude, station.latitude, station.longitude);
|
|
81
|
+
if (distance < minDistance) {
|
|
82
|
+
minDistance = distance;
|
|
83
|
+
nearest = {
|
|
84
|
+
...station,
|
|
85
|
+
distance
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return nearest;
|
|
90
|
+
}
|
|
91
|
+
function findNearestStations(latitude, longitude, stations, limit = 5) {
|
|
92
|
+
const withDistances = stations.map((station) => ({
|
|
93
|
+
...station,
|
|
94
|
+
distance: haversineDistance(latitude, longitude, station.latitude, station.longitude)
|
|
95
|
+
}));
|
|
96
|
+
withDistances.sort((a, b) => a.distance - b.distance);
|
|
97
|
+
return withDistances.slice(0, limit);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/utils/build-url.util.ts
|
|
102
|
+
function buildUrl(baseUrl, path, params) {
|
|
103
|
+
const url = new URL(baseUrl + path);
|
|
104
|
+
if (params) Object.entries(params).forEach(([key, value]) => {
|
|
105
|
+
if (value !== void 0) url.searchParams.set(key, String(value));
|
|
106
|
+
});
|
|
107
|
+
return url;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
//#endregion
|
|
111
|
+
//#region src/client/iqair-client.ts
|
|
112
|
+
var IQAirClient = class {
|
|
113
|
+
baseUrl;
|
|
114
|
+
locale;
|
|
115
|
+
userAgent;
|
|
116
|
+
constructor(config = {}) {
|
|
117
|
+
this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
118
|
+
this.locale = config.locale ?? DEFAULT_LOCALE;
|
|
119
|
+
this.userAgent = config.userAgent ?? DEFAULT_USER_AGENT;
|
|
120
|
+
}
|
|
121
|
+
async request(url) {
|
|
122
|
+
return fetchAndDecode(url, this.userAgent);
|
|
123
|
+
}
|
|
124
|
+
async getStations(params) {
|
|
125
|
+
const url = buildUrl(this.baseUrl, `/${this.locale}/profile/${params.dataSource}.data`, {
|
|
126
|
+
limit: params.limit ?? 500,
|
|
127
|
+
_routes: STATION_LIST_ROUTES
|
|
128
|
+
});
|
|
129
|
+
const stationsData = ((await this.request(url.toString()))["routes/$(locale).profile.$slug"]?.data)?.stationsMapData?.data;
|
|
130
|
+
if (!stationsData || stationsData.length === 0) return [];
|
|
131
|
+
return stationsData.map((station) => ({
|
|
132
|
+
id: station.id,
|
|
133
|
+
name: station.name,
|
|
134
|
+
url: station.url,
|
|
135
|
+
latitude: station.coordinates.latitude,
|
|
136
|
+
longitude: station.coordinates.longitude
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
async getStationAQI(params) {
|
|
140
|
+
const url = buildUrl(this.baseUrl, `/${this.locale}${params.stationUrl}.data`, { _routes: INDIVIDUAL_STATION_ROUTES });
|
|
141
|
+
const data = await this.request(url.toString());
|
|
142
|
+
const routeData = data.root?.data || data["routes/$"]?.data;
|
|
143
|
+
const details = routeData?.details;
|
|
144
|
+
if (!details || !details.current) throw new IQAirException("No station details found in response", 404);
|
|
145
|
+
const measurements = routeData?.measurements?.data?.measurements;
|
|
146
|
+
const historical = measurements ? {
|
|
147
|
+
hourly: measurements.hourly || [],
|
|
148
|
+
daily: measurements.daily || []
|
|
149
|
+
} : void 0;
|
|
150
|
+
return {
|
|
151
|
+
station: {
|
|
152
|
+
id: details.id,
|
|
153
|
+
name: details.name,
|
|
154
|
+
url: params.stationUrl,
|
|
155
|
+
distance: 0
|
|
156
|
+
},
|
|
157
|
+
current: {
|
|
158
|
+
ts: details.current.ts,
|
|
159
|
+
aqi: details.current.aqi,
|
|
160
|
+
mainPollutant: details.current.mainPollutant,
|
|
161
|
+
concentration: details.current.concentration,
|
|
162
|
+
condition: details.current.condition,
|
|
163
|
+
temperature: details.current.temperature,
|
|
164
|
+
humidity: details.current.humidity,
|
|
165
|
+
pressure: details.current.pressure,
|
|
166
|
+
wind: details.current.wind
|
|
167
|
+
},
|
|
168
|
+
pollutants: details.current.pollutants?.map((p) => ({
|
|
169
|
+
name: p.pollutantName,
|
|
170
|
+
aqi: p.aqi,
|
|
171
|
+
concentration: p.concentration,
|
|
172
|
+
unit: p.unit
|
|
173
|
+
})),
|
|
174
|
+
historical
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
async getNearestStation(params) {
|
|
178
|
+
const stations = await this.getStations({ dataSource: params.dataSource });
|
|
179
|
+
return findNearestStation(params.lat, params.lng, stations);
|
|
180
|
+
}
|
|
181
|
+
async getNearestStations(params) {
|
|
182
|
+
const stations = await this.getStations({ dataSource: params.dataSource });
|
|
183
|
+
return findNearestStations(params.lat, params.lng, stations, params.limit);
|
|
184
|
+
}
|
|
185
|
+
async getAQIForLocation(params) {
|
|
186
|
+
const nearestStation = await this.getNearestStation({
|
|
187
|
+
lat: params.lat,
|
|
188
|
+
lng: params.lng,
|
|
189
|
+
dataSource: params.dataSource
|
|
190
|
+
});
|
|
191
|
+
if (!nearestStation) return null;
|
|
192
|
+
const aqi = await this.getStationAQI({ stationUrl: nearestStation.url });
|
|
193
|
+
aqi.station.distance = nearestStation.distance;
|
|
194
|
+
return aqi;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/client/create-iqair-client.ts
|
|
200
|
+
function createIQAirClient(config) {
|
|
201
|
+
return new IQAirClient(config);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
//#endregion
|
|
205
|
+
export { IQAirClient, IQAirException, createIQAirClient };
|
|
206
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["nearest: StationWithDistance | null","withDistances: StationWithDistance[]"],"sources":["../src/constants/base-url.constant.ts","../src/constants/locale.constant.ts","../src/constants/routes.constant.ts","../src/constants/user-agent.constant.ts","../src/exceptions/iqair.exception.ts","../src/utils/decoder.util.ts","../src/utils/geo.util.ts","../src/utils/build-url.util.ts","../src/client/iqair-client.ts","../src/client/create-iqair-client.ts"],"sourcesContent":["export const DEFAULT_BASE_URL = \"https://www.iqair.com\";\n","export const DEFAULT_LOCALE = \"in-en\";\n","export const STATION_LIST_ROUTES = encodeURIComponent(\n \"routes/$(locale),routes/$(locale).profile.$slug\"\n);\n\nexport const INDIVIDUAL_STATION_ROUTES = encodeURIComponent(\"routes/$\");\n","export const DEFAULT_USER_AGENT =\n \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36\";\n","export class IQAirException extends Error {\n readonly statusCode: number;\n readonly body?: string;\n\n constructor(message: string, statusCode: number, body?: string) {\n super(message);\n this.name = \"IQAirException\";\n this.statusCode = statusCode;\n this.body = body;\n }\n}\n","import { UNSAFE_decodeViaTurboStream } from \"react-router\";\nimport { IQAirException } from \"../exceptions\";\n\nasync function resolveAllPromises<T>(obj: T): Promise<T> {\n if (obj instanceof Promise) {\n const resolved = await obj;\n return resolveAllPromises(resolved);\n }\n\n if (Array.isArray(obj)) {\n const resolved = await Promise.all(obj.map(resolveAllPromises));\n return resolved as T;\n }\n\n if (obj !== null && typeof obj === \"object\") {\n const entries = Object.entries(obj);\n const resolvedEntries = await Promise.all(\n entries.map(async ([key, value]) => [key, await resolveAllPromises(value)])\n );\n return Object.fromEntries(resolvedEntries) as T;\n }\n\n return obj;\n}\n\nexport async function decodeTurboStream<T>(\n stream: ReadableStream<Uint8Array>\n): Promise<T> {\n const decoded = await UNSAFE_decodeViaTurboStream(stream, globalThis);\n await decoded.done;\n const resolved = await resolveAllPromises(decoded.value);\n return resolved as T;\n}\n\nexport async function fetchAndDecode<T>(url: string, userAgent: string): Promise<T> {\n const response = await fetch(url, {\n headers: {\n \"User-Agent\": userAgent,\n Accept: \"*/*\",\n \"Accept-Language\": \"en-US,en;q=0.9\",\n },\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new IQAirException(\n `Request failed: ${response.status} ${response.statusText}`,\n response.status,\n text\n );\n }\n\n if (!response.body) {\n throw new IQAirException(\"Response body is null\", 500);\n }\n\n return decodeTurboStream<T>(response.body);\n}\n","import type { Station, StationWithDistance } from \"../types\";\n\nconst EARTH_RADIUS_KM = 6371;\n\nfunction toRadians(degrees: number): number {\n return degrees * (Math.PI / 180);\n}\n\nexport function haversineDistance(\n lat1: number,\n lon1: number,\n lat2: number,\n lon2: number\n): number {\n const dLat = toRadians(lat2 - lat1);\n const dLon = toRadians(lon2 - lon1);\n\n const a =\n Math.sin(dLat / 2) * Math.sin(dLat / 2) +\n Math.cos(toRadians(lat1)) *\n Math.cos(toRadians(lat2)) *\n Math.sin(dLon / 2) *\n Math.sin(dLon / 2);\n\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n\n return EARTH_RADIUS_KM * c;\n}\n\nexport function findNearestStation(\n latitude: number,\n longitude: number,\n stations: Station[]\n): StationWithDistance | null {\n if (stations.length === 0) {\n return null;\n }\n\n let nearest: StationWithDistance | null = null;\n let minDistance = Infinity;\n\n for (const station of stations) {\n const distance = haversineDistance(\n latitude,\n longitude,\n station.latitude,\n station.longitude\n );\n\n if (distance < minDistance) {\n minDistance = distance;\n nearest = { ...station, distance };\n }\n }\n\n return nearest;\n}\n\nexport function findNearestStations(\n latitude: number,\n longitude: number,\n stations: Station[],\n limit: number = 5\n): StationWithDistance[] {\n const withDistances: StationWithDistance[] = stations.map((station) => ({\n ...station,\n distance: haversineDistance(\n latitude,\n longitude,\n station.latitude,\n station.longitude\n ),\n }));\n\n withDistances.sort((a, b) => a.distance - b.distance);\n\n return withDistances.slice(0, limit);\n}\n","export function buildUrl(\n baseUrl: string,\n path: string,\n params?: Record<string, string | number | undefined>\n): URL {\n const url = new URL(baseUrl + path);\n\n if (params) {\n Object.entries(params).forEach(([key, value]) => {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n });\n }\n\n return url;\n}\n","import { IQAirClientConfig } from \"./iqair-client.config\";\nimport {\n DEFAULT_BASE_URL,\n DEFAULT_LOCALE,\n DEFAULT_USER_AGENT,\n STATION_LIST_ROUTES,\n INDIVIDUAL_STATION_ROUTES,\n} from \"../constants\";\nimport { IQAirException } from \"../exceptions\";\nimport { fetchAndDecode } from \"../utils/decoder.util\";\nimport { findNearestStation, findNearestStations } from \"../utils/geo.util\";\nimport { buildUrl } from \"../utils/build-url.util\";\nimport type {\n Station,\n StationWithDistance,\n AQIResponse,\n StationListResponse,\n IndividualStationResponse,\n GetStationsParams,\n GetStationAQIParams,\n GetNearestStationParams,\n GetNearestStationsParams,\n GetAQIForLocationParams,\n} from \"../types\";\n\nexport class IQAirClient {\n private readonly baseUrl: string;\n private readonly locale: string;\n private readonly userAgent: string;\n\n constructor(config: IQAirClientConfig = {}) {\n this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n this.locale = config.locale ?? DEFAULT_LOCALE;\n this.userAgent = config.userAgent ?? DEFAULT_USER_AGENT;\n }\n\n private async request<T>(url: string): Promise<T> {\n return fetchAndDecode<T>(url, this.userAgent);\n }\n\n async getStations(params: GetStationsParams): Promise<Station[]> {\n const url = buildUrl(\n this.baseUrl,\n `/${this.locale}/profile/${params.dataSource}.data`,\n {\n limit: params.limit ?? 500,\n _routes: STATION_LIST_ROUTES,\n }\n );\n\n const data = await this.request<StationListResponse>(url.toString());\n const routeData = data[\"routes/$(locale).profile.$slug\"]?.data;\n const stationsData = routeData?.stationsMapData?.data;\n\n if (!stationsData || stationsData.length === 0) {\n return [];\n }\n\n return stationsData.map((station) => ({\n id: station.id,\n name: station.name,\n url: station.url,\n latitude: station.coordinates.latitude,\n longitude: station.coordinates.longitude,\n }));\n }\n\n async getStationAQI(params: GetStationAQIParams): Promise<AQIResponse> {\n const url = buildUrl(\n this.baseUrl,\n `/${this.locale}${params.stationUrl}.data`,\n {\n _routes: INDIVIDUAL_STATION_ROUTES,\n }\n );\n\n const data = await this.request<IndividualStationResponse>(url.toString());\n const routeData = data.root?.data || data[\"routes/$\"]?.data;\n const details = routeData?.details;\n\n if (!details || !details.current) {\n throw new IQAirException(\"No station details found in response\", 404);\n }\n\n const measurements = routeData?.measurements?.data?.measurements;\n const historical = measurements\n ? {\n hourly: measurements.hourly || [],\n daily: measurements.daily || [],\n }\n : undefined;\n\n return {\n station: {\n id: details.id,\n name: details.name,\n url: params.stationUrl,\n distance: 0,\n },\n current: {\n ts: details.current.ts,\n aqi: details.current.aqi,\n mainPollutant: details.current.mainPollutant,\n concentration: details.current.concentration,\n condition: details.current.condition,\n temperature: details.current.temperature,\n humidity: details.current.humidity,\n pressure: details.current.pressure,\n wind: details.current.wind,\n },\n pollutants: details.current.pollutants?.map((p) => ({\n name: p.pollutantName,\n aqi: p.aqi,\n concentration: p.concentration,\n unit: p.unit,\n })),\n historical,\n };\n }\n\n async getNearestStation(\n params: GetNearestStationParams\n ): Promise<StationWithDistance | null> {\n const stations = await this.getStations({ dataSource: params.dataSource });\n return findNearestStation(params.lat, params.lng, stations);\n }\n\n async getNearestStations(\n params: GetNearestStationsParams\n ): Promise<StationWithDistance[]> {\n const stations = await this.getStations({ dataSource: params.dataSource });\n return findNearestStations(params.lat, params.lng, stations, params.limit);\n }\n\n async getAQIForLocation(\n params: GetAQIForLocationParams\n ): Promise<AQIResponse | null> {\n const nearestStation = await this.getNearestStation({\n lat: params.lat,\n lng: params.lng,\n dataSource: params.dataSource,\n });\n\n if (!nearestStation) {\n return null;\n }\n\n const aqi = await this.getStationAQI({ stationUrl: nearestStation.url });\n aqi.station.distance = nearestStation.distance;\n return aqi;\n }\n}\n","import { IQAirClient } from \"./iqair-client\";\nimport { IQAirClientConfig } from \"./iqair-client.config\";\n\nexport function createIQAirClient(config?: IQAirClientConfig): IQAirClient {\n return new IQAirClient(config);\n}\n"],"mappings":";;;AAAA,MAAa,mBAAmB;;;;ACAhC,MAAa,iBAAiB;;;;ACA9B,MAAa,sBAAsB,mBACjC,kDACD;AAED,MAAa,4BAA4B,mBAAmB,WAAW;;;;ACJvE,MAAa,qBACX;;;;ACDF,IAAa,iBAAb,cAAoC,MAAM;CACxC,AAAS;CACT,AAAS;CAET,YAAY,SAAiB,YAAoB,MAAe;AAC9D,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,aAAa;AAClB,OAAK,OAAO;;;;;;ACLhB,eAAe,mBAAsB,KAAoB;AACvD,KAAI,eAAe,QAEjB,QAAO,mBADU,MAAM,IACY;AAGrC,KAAI,MAAM,QAAQ,IAAI,CAEpB,QADiB,MAAM,QAAQ,IAAI,IAAI,IAAI,mBAAmB,CAAC;AAIjE,KAAI,QAAQ,QAAQ,OAAO,QAAQ,UAAU;EAC3C,MAAM,UAAU,OAAO,QAAQ,IAAI;EACnC,MAAM,kBAAkB,MAAM,QAAQ,IACpC,QAAQ,IAAI,OAAO,CAAC,KAAK,WAAW,CAAC,KAAK,MAAM,mBAAmB,MAAM,CAAC,CAAC,CAC5E;AACD,SAAO,OAAO,YAAY,gBAAgB;;AAG5C,QAAO;;AAGT,eAAsB,kBACpB,QACY;CACZ,MAAM,UAAU,MAAM,4BAA4B,QAAQ,WAAW;AACrE,OAAM,QAAQ;AAEd,QADiB,MAAM,mBAAmB,QAAQ,MAAM;;AAI1D,eAAsB,eAAkB,KAAa,WAA+B;CAClF,MAAM,WAAW,MAAM,MAAM,KAAK,EAChC,SAAS;EACP,cAAc;EACd,QAAQ;EACR,mBAAmB;EACpB,EACF,CAAC;AAEF,KAAI,CAAC,SAAS,IAAI;EAChB,MAAM,OAAO,MAAM,SAAS,MAAM;AAClC,QAAM,IAAI,eACR,mBAAmB,SAAS,OAAO,GAAG,SAAS,cAC/C,SAAS,QACT,KACD;;AAGH,KAAI,CAAC,SAAS,KACZ,OAAM,IAAI,eAAe,yBAAyB,IAAI;AAGxD,QAAO,kBAAqB,SAAS,KAAK;;;;;ACtD5C,MAAM,kBAAkB;AAExB,SAAS,UAAU,SAAyB;AAC1C,QAAO,WAAW,KAAK,KAAK;;AAG9B,SAAgB,kBACd,MACA,MACA,MACA,MACQ;CACR,MAAM,OAAO,UAAU,OAAO,KAAK;CACnC,MAAM,OAAO,UAAU,OAAO,KAAK;CAEnC,MAAM,IACJ,KAAK,IAAI,OAAO,EAAE,GAAG,KAAK,IAAI,OAAO,EAAE,GACvC,KAAK,IAAI,UAAU,KAAK,CAAC,GACvB,KAAK,IAAI,UAAU,KAAK,CAAC,GACzB,KAAK,IAAI,OAAO,EAAE,GAClB,KAAK,IAAI,OAAO,EAAE;AAItB,QAAO,mBAFG,IAAI,KAAK,MAAM,KAAK,KAAK,EAAE,EAAE,KAAK,KAAK,IAAI,EAAE,CAAC;;AAK1D,SAAgB,mBACd,UACA,WACA,UAC4B;AAC5B,KAAI,SAAS,WAAW,EACtB,QAAO;CAGT,IAAIA,UAAsC;CAC1C,IAAI,cAAc;AAElB,MAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,WAAW,kBACf,UACA,WACA,QAAQ,UACR,QAAQ,UACT;AAED,MAAI,WAAW,aAAa;AAC1B,iBAAc;AACd,aAAU;IAAE,GAAG;IAAS;IAAU;;;AAItC,QAAO;;AAGT,SAAgB,oBACd,UACA,WACA,UACA,QAAgB,GACO;CACvB,MAAMC,gBAAuC,SAAS,KAAK,aAAa;EACtE,GAAG;EACH,UAAU,kBACR,UACA,WACA,QAAQ,UACR,QAAQ,UACT;EACF,EAAE;AAEH,eAAc,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,SAAS;AAErD,QAAO,cAAc,MAAM,GAAG,MAAM;;;;;AC5EtC,SAAgB,SACd,SACA,MACA,QACK;CACL,MAAM,MAAM,IAAI,IAAI,UAAU,KAAK;AAEnC,KAAI,OACF,QAAO,QAAQ,OAAO,CAAC,SAAS,CAAC,KAAK,WAAW;AAC/C,MAAI,UAAU,OACZ,KAAI,aAAa,IAAI,KAAK,OAAO,MAAM,CAAC;GAE1C;AAGJ,QAAO;;;;;ACUT,IAAa,cAAb,MAAyB;CACvB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,YAAY,SAA4B,EAAE,EAAE;AAC1C,OAAK,UAAU,OAAO,WAAW;AACjC,OAAK,SAAS,OAAO,UAAU;AAC/B,OAAK,YAAY,OAAO,aAAa;;CAGvC,MAAc,QAAW,KAAyB;AAChD,SAAO,eAAkB,KAAK,KAAK,UAAU;;CAG/C,MAAM,YAAY,QAA+C;EAC/D,MAAM,MAAM,SACV,KAAK,SACL,IAAI,KAAK,OAAO,WAAW,OAAO,WAAW,QAC7C;GACE,OAAO,OAAO,SAAS;GACvB,SAAS;GACV,CACF;EAID,MAAM,iBAFO,MAAM,KAAK,QAA6B,IAAI,UAAU,CAAC,EAC7C,mCAAmC,OAC1B,iBAAiB;AAEjD,MAAI,CAAC,gBAAgB,aAAa,WAAW,EAC3C,QAAO,EAAE;AAGX,SAAO,aAAa,KAAK,aAAa;GACpC,IAAI,QAAQ;GACZ,MAAM,QAAQ;GACd,KAAK,QAAQ;GACb,UAAU,QAAQ,YAAY;GAC9B,WAAW,QAAQ,YAAY;GAChC,EAAE;;CAGL,MAAM,cAAc,QAAmD;EACrE,MAAM,MAAM,SACV,KAAK,SACL,IAAI,KAAK,SAAS,OAAO,WAAW,QACpC,EACE,SAAS,2BACV,CACF;EAED,MAAM,OAAO,MAAM,KAAK,QAAmC,IAAI,UAAU,CAAC;EAC1E,MAAM,YAAY,KAAK,MAAM,QAAQ,KAAK,aAAa;EACvD,MAAM,UAAU,WAAW;AAE3B,MAAI,CAAC,WAAW,CAAC,QAAQ,QACvB,OAAM,IAAI,eAAe,wCAAwC,IAAI;EAGvE,MAAM,eAAe,WAAW,cAAc,MAAM;EACpD,MAAM,aAAa,eACf;GACE,QAAQ,aAAa,UAAU,EAAE;GACjC,OAAO,aAAa,SAAS,EAAE;GAChC,GACD;AAEJ,SAAO;GACL,SAAS;IACP,IAAI,QAAQ;IACZ,MAAM,QAAQ;IACd,KAAK,OAAO;IACZ,UAAU;IACX;GACD,SAAS;IACP,IAAI,QAAQ,QAAQ;IACpB,KAAK,QAAQ,QAAQ;IACrB,eAAe,QAAQ,QAAQ;IAC/B,eAAe,QAAQ,QAAQ;IAC/B,WAAW,QAAQ,QAAQ;IAC3B,aAAa,QAAQ,QAAQ;IAC7B,UAAU,QAAQ,QAAQ;IAC1B,UAAU,QAAQ,QAAQ;IAC1B,MAAM,QAAQ,QAAQ;IACvB;GACD,YAAY,QAAQ,QAAQ,YAAY,KAAK,OAAO;IAClD,MAAM,EAAE;IACR,KAAK,EAAE;IACP,eAAe,EAAE;IACjB,MAAM,EAAE;IACT,EAAE;GACH;GACD;;CAGH,MAAM,kBACJ,QACqC;EACrC,MAAM,WAAW,MAAM,KAAK,YAAY,EAAE,YAAY,OAAO,YAAY,CAAC;AAC1E,SAAO,mBAAmB,OAAO,KAAK,OAAO,KAAK,SAAS;;CAG7D,MAAM,mBACJ,QACgC;EAChC,MAAM,WAAW,MAAM,KAAK,YAAY,EAAE,YAAY,OAAO,YAAY,CAAC;AAC1E,SAAO,oBAAoB,OAAO,KAAK,OAAO,KAAK,UAAU,OAAO,MAAM;;CAG5E,MAAM,kBACJ,QAC6B;EAC7B,MAAM,iBAAiB,MAAM,KAAK,kBAAkB;GAClD,KAAK,OAAO;GACZ,KAAK,OAAO;GACZ,YAAY,OAAO;GACpB,CAAC;AAEF,MAAI,CAAC,eACH,QAAO;EAGT,MAAM,MAAM,MAAM,KAAK,cAAc,EAAE,YAAY,eAAe,KAAK,CAAC;AACxE,MAAI,QAAQ,WAAW,eAAe;AACtC,SAAO;;;;;;AClJX,SAAgB,kBAAkB,QAAyC;AACzE,QAAO,IAAI,YAAY,OAAO"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "iqair-api",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.0.1",
|
|
5
|
+
"description": "Fully typed TypeScript SDK for scraping IQAir air quality data",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/neo773/iqair-api.git"
|
|
9
|
+
},
|
|
10
|
+
"author": "neo773",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./dist/index.mjs",
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/index.mjs",
|
|
16
|
+
"module": "./dist/index.mjs",
|
|
17
|
+
"types": "./dist/index.d.mts",
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsdown src/index.ts",
|
|
23
|
+
"dev": "tsdown src/index.ts --watch",
|
|
24
|
+
"test": "bun test",
|
|
25
|
+
"test:unit": "bun test tests/unit",
|
|
26
|
+
"test:e2e": "bun test tests/e2e",
|
|
27
|
+
"typecheck": "tsc --noEmit"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"iqair",
|
|
31
|
+
"air-quality",
|
|
32
|
+
"aqi",
|
|
33
|
+
"pollution",
|
|
34
|
+
"pm25",
|
|
35
|
+
"api",
|
|
36
|
+
"sdk"
|
|
37
|
+
],
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"react-router": "^7.6.1",
|
|
41
|
+
"turbo-stream": "^2.4.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/bun": "^1.3.8",
|
|
45
|
+
"tsdown": "^0.18.1",
|
|
46
|
+
"typescript": "^5.9.3"
|
|
47
|
+
},
|
|
48
|
+
"directories": {
|
|
49
|
+
"example": "examples",
|
|
50
|
+
"test": "tests"
|
|
51
|
+
},
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/neo773/iqair-api/issues"
|
|
54
|
+
},
|
|
55
|
+
"homepage": "https://github.com/neo773/iqair-api#readme"
|
|
56
|
+
}
|