weather.ts 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/dist/index.js ADDED
@@ -0,0 +1,401 @@
1
+ // src/core/errors.ts
2
+ var OpenMeteoError = class extends Error {
3
+ constructor(message, status, url, reason) {
4
+ super(message);
5
+ this.message = message;
6
+ this.status = status;
7
+ this.url = url;
8
+ this.reason = reason;
9
+ this.name = "OpenMeteoError";
10
+ }
11
+ };
12
+
13
+ // src/core/config.ts
14
+ var globalConfig = {
15
+ userAgent: "weather.ts/1.0.0",
16
+ retries: 3
17
+ };
18
+ function setConfig(config) {
19
+ globalConfig = { ...globalConfig, ...config };
20
+ }
21
+ function getConfig() {
22
+ return globalConfig;
23
+ }
24
+
25
+ // src/helpers/transformers.ts
26
+ function groupTimeSeries(data) {
27
+ if (!data || !Array.isArray(data.time)) {
28
+ return [];
29
+ }
30
+ const times = data.time;
31
+ const keys = Object.keys(data).filter(
32
+ (key) => key !== "time" && !key.endsWith("_units") && Array.isArray(data[key])
33
+ );
34
+ return times.map((time, index) => {
35
+ const entry = { time };
36
+ for (const key of keys) {
37
+ if (data[key].length > index) {
38
+ entry[key] = data[key][index];
39
+ } else {
40
+ entry[key] = null;
41
+ }
42
+ }
43
+ return entry;
44
+ });
45
+ }
46
+ function formatDate(date) {
47
+ const year = date.getUTCFullYear();
48
+ const month = String(date.getUTCMonth() + 1).padStart(2, "0");
49
+ const day = String(date.getUTCDate()).padStart(2, "0");
50
+ return `${year}-${month}-${day}`;
51
+ }
52
+
53
+ // src/helpers/params.ts
54
+ function sanitizeParams(params) {
55
+ const sanitized = {};
56
+ for (const [key, value] of Object.entries(params)) {
57
+ if (value === void 0 || value === null) {
58
+ continue;
59
+ }
60
+ if (value instanceof Date) {
61
+ sanitized[key] = formatDate(value);
62
+ } else if (Array.isArray(value)) {
63
+ sanitized[key] = value.map((v) => v instanceof Date ? formatDate(v) : String(v)).join(",");
64
+ } else {
65
+ sanitized[key] = String(value);
66
+ }
67
+ }
68
+ return sanitized;
69
+ }
70
+
71
+ // src/core/fetcher.ts
72
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
73
+ async function openMeteoFetch(url, options = {}) {
74
+ const globalConfig2 = getConfig();
75
+ const {
76
+ params,
77
+ userAgent = globalConfig2.userAgent,
78
+ retries = globalConfig2.retries,
79
+ ...init
80
+ } = options;
81
+ const sanitizedParams = params ? sanitizeParams(params) : {};
82
+ const searchParams = new URLSearchParams(sanitizedParams);
83
+ const queryString = searchParams.toString();
84
+ const finalUrl = queryString ? `${url}?${queryString}` : url;
85
+ const headers = new Headers(init.headers);
86
+ const isBrowser = typeof window !== "undefined";
87
+ if (userAgent && !isBrowser) {
88
+ headers.set("User-Agent", userAgent);
89
+ }
90
+ let lastError;
91
+ const maxAttempts = retries || 0;
92
+ for (let attempt = 0; attempt <= maxAttempts; attempt++) {
93
+ try {
94
+ const response = await fetch(finalUrl, { ...init, headers });
95
+ if (response.ok) {
96
+ return await response.json();
97
+ }
98
+ const status = response.status;
99
+ let reason;
100
+ const contentType = response.headers.get("content-type");
101
+ try {
102
+ if (contentType && contentType.includes("application/json")) {
103
+ const errorData = await response.json();
104
+ reason = errorData.reason || errorData.message;
105
+ } else {
106
+ reason = await response.text();
107
+ if (reason && reason.length > 200) {
108
+ reason = reason.substring(0, 200) + "...";
109
+ }
110
+ }
111
+ } catch {
112
+ }
113
+ const error = new OpenMeteoError(
114
+ `Open-Meteo API Error: ${status}${reason ? ` - ${reason}` : ""}`,
115
+ status,
116
+ finalUrl,
117
+ reason
118
+ );
119
+ if (attempt < maxAttempts && (status === 429 || status >= 500)) {
120
+ const backoff = Math.pow(2, attempt) * 500;
121
+ await sleep(backoff);
122
+ continue;
123
+ }
124
+ throw error;
125
+ } catch (err) {
126
+ if (err instanceof OpenMeteoError) throw err;
127
+ lastError = err;
128
+ if (attempt < maxAttempts) {
129
+ const backoff = Math.pow(2, attempt) * 500;
130
+ await sleep(backoff);
131
+ continue;
132
+ }
133
+ }
134
+ }
135
+ throw new OpenMeteoError(
136
+ `Network error or max retries reached: ${lastError?.message || "Unknown error"}`,
137
+ void 0,
138
+ finalUrl
139
+ );
140
+ }
141
+
142
+ // src/helpers/module-factory.ts
143
+ function createModule(baseUrl) {
144
+ return async (params, options = {}) => {
145
+ return openMeteoFetch(baseUrl, { ...options, params });
146
+ };
147
+ }
148
+
149
+ // src/modules/forecast/index.ts
150
+ var forecast = createModule(
151
+ "https://api.open-meteo.com/v1/forecast"
152
+ );
153
+
154
+ // src/modules/air-quality/index.ts
155
+ var airQuality = createModule(
156
+ "https://air-quality-api.open-meteo.com/v1/air-quality"
157
+ );
158
+
159
+ // src/modules/geocoding/index.ts
160
+ var geocodingSearch = createModule("https://geocoding-api.open-meteo.com/v1/search");
161
+ var geocodingGet = createModule("https://geocoding-api.open-meteo.com/v1/get");
162
+
163
+ // src/modules/historical/index.ts
164
+ var historical = createModule(
165
+ "https://archive-api.open-meteo.com/v1/archive"
166
+ );
167
+
168
+ // src/modules/elevation/index.ts
169
+ var elevation = createModule(
170
+ "https://api.open-meteo.com/v1/elevation"
171
+ );
172
+
173
+ // src/modules/marine/index.ts
174
+ var marine = createModule(
175
+ "https://marine-api.open-meteo.com/v1/marine"
176
+ );
177
+
178
+ // src/modules/flood/index.ts
179
+ var flood = createModule(
180
+ "https://flood-api.open-meteo.com/v1/flood"
181
+ );
182
+
183
+ // src/modules/ensemble/index.ts
184
+ var ensemble = createModule(
185
+ "https://ensemble-api.open-meteo.com/v1/ensemble"
186
+ );
187
+
188
+ // src/modules/climate/index.ts
189
+ var climate = createModule(
190
+ "https://climate-api.open-meteo.com/v1/climate"
191
+ );
192
+
193
+ // src/modules/seasonal/index.ts
194
+ var seasonal = createModule(
195
+ "https://seasonal-api.open-meteo.com/v1/seasonal"
196
+ );
197
+
198
+ // src/core/client.ts
199
+ var OpenMeteoClient = class {
200
+ constructor(config = {}) {
201
+ this.config = {
202
+ userAgent: "weather.ts/1.0.0",
203
+ retries: 3,
204
+ ...config
205
+ };
206
+ }
207
+ getOptions(options = {}) {
208
+ return {
209
+ userAgent: this.config.userAgent,
210
+ retries: this.config.retries,
211
+ ...options
212
+ };
213
+ }
214
+ async forecast(params, options) {
215
+ return forecast(params, this.getOptions(options));
216
+ }
217
+ async airQuality(params, options) {
218
+ return airQuality(params, this.getOptions(options));
219
+ }
220
+ async geocodingSearch(params, options) {
221
+ return geocodingSearch(params, this.getOptions(options));
222
+ }
223
+ async geocodingGet(params, options) {
224
+ return geocodingGet(params, this.getOptions(options));
225
+ }
226
+ async historical(params, options) {
227
+ return historical(params, this.getOptions(options));
228
+ }
229
+ async elevation(params, options) {
230
+ return elevation(params, this.getOptions(options));
231
+ }
232
+ async marine(params, options) {
233
+ return marine(params, this.getOptions(options));
234
+ }
235
+ async flood(params, options) {
236
+ return flood(params, this.getOptions(options));
237
+ }
238
+ async ensemble(params, options) {
239
+ return ensemble(params, this.getOptions(options));
240
+ }
241
+ async climate(params, options) {
242
+ return climate(params, this.getOptions(options));
243
+ }
244
+ async seasonal(params, options) {
245
+ return seasonal(params, this.getOptions(options));
246
+ }
247
+ };
248
+
249
+ // src/helpers/wmoCode.ts
250
+ function translateWmoCode(code) {
251
+ switch (code) {
252
+ case 0:
253
+ return "Clear sky";
254
+ case 1:
255
+ return "Mainly clear";
256
+ case 2:
257
+ return "Partly cloudy";
258
+ case 3:
259
+ return "Overcast";
260
+ case 45:
261
+ return "Fog";
262
+ case 48:
263
+ return "Depositing rime fog";
264
+ case 51:
265
+ return "Light drizzle";
266
+ case 53:
267
+ return "Moderate drizzle";
268
+ case 55:
269
+ return "Dense drizzle";
270
+ case 56:
271
+ return "Light freezing drizzle";
272
+ case 57:
273
+ return "Dense freezing drizzle";
274
+ case 61:
275
+ return "Slight rain";
276
+ case 63:
277
+ return "Moderate rain";
278
+ case 65:
279
+ return "Heavy rain";
280
+ case 66:
281
+ return "Light freezing rain";
282
+ case 67:
283
+ return "Heavy freezing rain";
284
+ case 71:
285
+ return "Slight snow fall";
286
+ case 73:
287
+ return "Moderate snow fall";
288
+ case 75:
289
+ return "Heavy snow fall";
290
+ case 77:
291
+ return "Snow grains";
292
+ case 80:
293
+ return "Slight rain showers";
294
+ case 81:
295
+ return "Moderate rain showers";
296
+ case 82:
297
+ return "Violent rain showers";
298
+ case 85:
299
+ return "Slight snow showers";
300
+ case 86:
301
+ return "Heavy snow showers";
302
+ case 95:
303
+ return "Thunderstorm";
304
+ case 96:
305
+ return "Thunderstorm with slight hail";
306
+ case 99:
307
+ return "Thunderstorm with heavy hail";
308
+ default:
309
+ return "Unknown";
310
+ }
311
+ }
312
+ function getWmoCodeIdentifier(code) {
313
+ switch (code) {
314
+ case 0:
315
+ return "sunny";
316
+ case 1:
317
+ case 2:
318
+ return "partlyCloudy";
319
+ case 3:
320
+ return "cloudy";
321
+ case 45:
322
+ case 48:
323
+ return "fog";
324
+ case 51:
325
+ case 53:
326
+ case 55:
327
+ case 56:
328
+ case 57:
329
+ return "drizzle";
330
+ case 61:
331
+ case 63:
332
+ case 65:
333
+ case 66:
334
+ case 67:
335
+ case 80:
336
+ case 81:
337
+ case 82:
338
+ return "rainy";
339
+ case 71:
340
+ case 73:
341
+ case 75:
342
+ case 77:
343
+ case 85:
344
+ case 86:
345
+ return "snowy";
346
+ case 95:
347
+ case 96:
348
+ case 99:
349
+ return "thunderstorm";
350
+ default:
351
+ return "unknown";
352
+ }
353
+ }
354
+
355
+ // src/helpers/enrichers.ts
356
+ var WMO_CODE_KEYS = [
357
+ "weathercode",
358
+ "weather_code",
359
+ "daily_weather_code",
360
+ "hourly_weather_code"
361
+ ];
362
+ function enrichWmoCodes(data) {
363
+ const enriched = { ...data };
364
+ for (const key of Object.keys(data)) {
365
+ if (WMO_CODE_KEYS.includes(key) && Array.isArray(data[key])) {
366
+ enriched[`${key}_description`] = data[key].map(
367
+ (code) => translateWmoCode(code)
368
+ );
369
+ enriched[`${key}_identifier`] = data[key].map(
370
+ (code) => getWmoCodeIdentifier(code)
371
+ );
372
+ } else if (WMO_CODE_KEYS.includes(key) && typeof data[key] === "number") {
373
+ enriched[`${key}_description`] = translateWmoCode(data[key]);
374
+ enriched[`${key}_identifier`] = getWmoCodeIdentifier(data[key]);
375
+ }
376
+ }
377
+ return enriched;
378
+ }
379
+ export {
380
+ OpenMeteoClient,
381
+ OpenMeteoError,
382
+ airQuality,
383
+ climate,
384
+ elevation,
385
+ enrichWmoCodes,
386
+ ensemble,
387
+ flood,
388
+ forecast,
389
+ formatDate,
390
+ geocodingGet,
391
+ geocodingSearch,
392
+ getConfig,
393
+ getWmoCodeIdentifier,
394
+ groupTimeSeries,
395
+ historical,
396
+ marine,
397
+ openMeteoFetch,
398
+ seasonal,
399
+ setConfig,
400
+ translateWmoCode
401
+ };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "weather.ts",
3
+ "version": "1.0.0",
4
+ "description": "The perfectest library for weather.",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "type": "module",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "dev": "tsup src/index.ts --watch",
20
+ "build": "tsup src/index.ts --format esm --dts",
21
+ "format": "prettier --write .",
22
+ "prepublishOnly": "npm run format && npm run build"
23
+ },
24
+ "keywords": [
25
+ "meteo",
26
+ "weather",
27
+ "wrapper",
28
+ "api",
29
+ "open-meteo"
30
+ ],
31
+ "author": "adouche-js",
32
+ "license": "LGPL-3.0-or-later",
33
+ "devDependencies": {
34
+ "@types/bun": "latest",
35
+ "prettier": "^3.8.3",
36
+ "tsup": "^8.5.1"
37
+ },
38
+ "peerDependencies": {
39
+ "typescript": "^5"
40
+ }
41
+ }