@jablum/weather-mcp 1.7.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.
Files changed (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1319 -0
  3. package/dist/analytics/anonymizer.d.ts +37 -0
  4. package/dist/analytics/anonymizer.d.ts.map +1 -0
  5. package/dist/analytics/anonymizer.js +112 -0
  6. package/dist/analytics/anonymizer.js.map +1 -0
  7. package/dist/analytics/collector.d.ts +72 -0
  8. package/dist/analytics/collector.d.ts.map +1 -0
  9. package/dist/analytics/collector.js +282 -0
  10. package/dist/analytics/collector.js.map +1 -0
  11. package/dist/analytics/config.d.ts +15 -0
  12. package/dist/analytics/config.d.ts.map +1 -0
  13. package/dist/analytics/config.js +172 -0
  14. package/dist/analytics/config.js.map +1 -0
  15. package/dist/analytics/index.d.ts +8 -0
  16. package/dist/analytics/index.d.ts.map +1 -0
  17. package/dist/analytics/index.js +7 -0
  18. package/dist/analytics/index.js.map +1 -0
  19. package/dist/analytics/middleware.d.ts +33 -0
  20. package/dist/analytics/middleware.d.ts.map +1 -0
  21. package/dist/analytics/middleware.js +99 -0
  22. package/dist/analytics/middleware.js.map +1 -0
  23. package/dist/analytics/transport.d.ts +11 -0
  24. package/dist/analytics/transport.d.ts.map +1 -0
  25. package/dist/analytics/transport.js +92 -0
  26. package/dist/analytics/transport.js.map +1 -0
  27. package/dist/analytics/types.d.ts +74 -0
  28. package/dist/analytics/types.d.ts.map +1 -0
  29. package/dist/analytics/types.js +6 -0
  30. package/dist/analytics/types.js.map +1 -0
  31. package/dist/config/api.d.ts +30 -0
  32. package/dist/config/api.d.ts.map +1 -0
  33. package/dist/config/api.js +32 -0
  34. package/dist/config/api.js.map +1 -0
  35. package/dist/config/cache.d.ts +31 -0
  36. package/dist/config/cache.d.ts.map +1 -0
  37. package/dist/config/cache.js +108 -0
  38. package/dist/config/cache.js.map +1 -0
  39. package/dist/config/displayThresholds.d.ts +83 -0
  40. package/dist/config/displayThresholds.d.ts.map +1 -0
  41. package/dist/config/displayThresholds.js +83 -0
  42. package/dist/config/displayThresholds.js.map +1 -0
  43. package/dist/config/tools.d.ts +44 -0
  44. package/dist/config/tools.d.ts.map +1 -0
  45. package/dist/config/tools.js +269 -0
  46. package/dist/config/tools.js.map +1 -0
  47. package/dist/errors/ApiError.d.ts +62 -0
  48. package/dist/errors/ApiError.d.ts.map +1 -0
  49. package/dist/errors/ApiError.js +171 -0
  50. package/dist/errors/ApiError.js.map +1 -0
  51. package/dist/handlers/airQualityHandler.d.ts +11 -0
  52. package/dist/handlers/airQualityHandler.d.ts.map +1 -0
  53. package/dist/handlers/airQualityHandler.js +154 -0
  54. package/dist/handlers/airQualityHandler.js.map +1 -0
  55. package/dist/handlers/alertsHandler.d.ts +11 -0
  56. package/dist/handlers/alertsHandler.d.ts.map +1 -0
  57. package/dist/handlers/alertsHandler.js +98 -0
  58. package/dist/handlers/alertsHandler.js.map +1 -0
  59. package/dist/handlers/currentConditionsHandler.d.ts +13 -0
  60. package/dist/handlers/currentConditionsHandler.d.ts.map +1 -0
  61. package/dist/handlers/currentConditionsHandler.js +296 -0
  62. package/dist/handlers/currentConditionsHandler.js.map +1 -0
  63. package/dist/handlers/forecastHandler.d.ts +16 -0
  64. package/dist/handlers/forecastHandler.d.ts.map +1 -0
  65. package/dist/handlers/forecastHandler.js +454 -0
  66. package/dist/handlers/forecastHandler.js.map +1 -0
  67. package/dist/handlers/historicalWeatherHandler.d.ts +12 -0
  68. package/dist/handlers/historicalWeatherHandler.d.ts.map +1 -0
  69. package/dist/handlers/historicalWeatherHandler.js +188 -0
  70. package/dist/handlers/historicalWeatherHandler.js.map +1 -0
  71. package/dist/handlers/lightningHandler.d.ts +14 -0
  72. package/dist/handlers/lightningHandler.d.ts.map +1 -0
  73. package/dist/handlers/lightningHandler.js +258 -0
  74. package/dist/handlers/lightningHandler.js.map +1 -0
  75. package/dist/handlers/locationHandler.d.ts +12 -0
  76. package/dist/handlers/locationHandler.d.ts.map +1 -0
  77. package/dist/handlers/locationHandler.js +149 -0
  78. package/dist/handlers/locationHandler.js.map +1 -0
  79. package/dist/handlers/marineConditionsHandler.d.ts +13 -0
  80. package/dist/handlers/marineConditionsHandler.d.ts.map +1 -0
  81. package/dist/handlers/marineConditionsHandler.js +270 -0
  82. package/dist/handlers/marineConditionsHandler.js.map +1 -0
  83. package/dist/handlers/riverConditionsHandler.d.ts +11 -0
  84. package/dist/handlers/riverConditionsHandler.d.ts.map +1 -0
  85. package/dist/handlers/riverConditionsHandler.js +176 -0
  86. package/dist/handlers/riverConditionsHandler.js.map +1 -0
  87. package/dist/handlers/savedLocationsHandler.d.ts +50 -0
  88. package/dist/handlers/savedLocationsHandler.d.ts.map +1 -0
  89. package/dist/handlers/savedLocationsHandler.js +397 -0
  90. package/dist/handlers/savedLocationsHandler.js.map +1 -0
  91. package/dist/handlers/statusHandler.d.ts +12 -0
  92. package/dist/handlers/statusHandler.d.ts.map +1 -0
  93. package/dist/handlers/statusHandler.js +115 -0
  94. package/dist/handlers/statusHandler.js.map +1 -0
  95. package/dist/handlers/weatherImageryHandler.d.ts +14 -0
  96. package/dist/handlers/weatherImageryHandler.d.ts.map +1 -0
  97. package/dist/handlers/weatherImageryHandler.js +143 -0
  98. package/dist/handlers/weatherImageryHandler.js.map +1 -0
  99. package/dist/handlers/wildfireHandler.d.ts +11 -0
  100. package/dist/handlers/wildfireHandler.d.ts.map +1 -0
  101. package/dist/handlers/wildfireHandler.js +186 -0
  102. package/dist/handlers/wildfireHandler.js.map +1 -0
  103. package/dist/index.d.ts +7 -0
  104. package/dist/index.d.ts.map +1 -0
  105. package/dist/index.js +735 -0
  106. package/dist/index.js.map +1 -0
  107. package/dist/services/blitzortung.d.ts +67 -0
  108. package/dist/services/blitzortung.d.ts.map +1 -0
  109. package/dist/services/blitzortung.js +475 -0
  110. package/dist/services/blitzortung.js.map +1 -0
  111. package/dist/services/geocoding.d.ts +57 -0
  112. package/dist/services/geocoding.d.ts.map +1 -0
  113. package/dist/services/geocoding.js +393 -0
  114. package/dist/services/geocoding.js.map +1 -0
  115. package/dist/services/locationStore.d.ts +62 -0
  116. package/dist/services/locationStore.d.ts.map +1 -0
  117. package/dist/services/locationStore.js +201 -0
  118. package/dist/services/locationStore.js.map +1 -0
  119. package/dist/services/ncei.d.ts +61 -0
  120. package/dist/services/ncei.d.ts.map +1 -0
  121. package/dist/services/ncei.js +126 -0
  122. package/dist/services/ncei.js.map +1 -0
  123. package/dist/services/nifc.d.ts +44 -0
  124. package/dist/services/nifc.d.ts.map +1 -0
  125. package/dist/services/nifc.js +159 -0
  126. package/dist/services/nifc.js.map +1 -0
  127. package/dist/services/noaa.d.ts +161 -0
  128. package/dist/services/noaa.d.ts.map +1 -0
  129. package/dist/services/noaa.js +681 -0
  130. package/dist/services/noaa.js.map +1 -0
  131. package/dist/services/nominatim.d.ts +62 -0
  132. package/dist/services/nominatim.d.ts.map +1 -0
  133. package/dist/services/nominatim.js +254 -0
  134. package/dist/services/nominatim.js.map +1 -0
  135. package/dist/services/openmeteo.d.ts +189 -0
  136. package/dist/services/openmeteo.d.ts.map +1 -0
  137. package/dist/services/openmeteo.js +936 -0
  138. package/dist/services/openmeteo.js.map +1 -0
  139. package/dist/services/rainviewer.d.ts +37 -0
  140. package/dist/services/rainviewer.d.ts.map +1 -0
  141. package/dist/services/rainviewer.js +115 -0
  142. package/dist/services/rainviewer.js.map +1 -0
  143. package/dist/types/imagery.d.ts +82 -0
  144. package/dist/types/imagery.d.ts.map +1 -0
  145. package/dist/types/imagery.js +6 -0
  146. package/dist/types/imagery.js.map +1 -0
  147. package/dist/types/lightning.d.ts +89 -0
  148. package/dist/types/lightning.d.ts.map +1 -0
  149. package/dist/types/lightning.js +6 -0
  150. package/dist/types/lightning.js.map +1 -0
  151. package/dist/types/noaa.d.ts +535 -0
  152. package/dist/types/noaa.d.ts.map +1 -0
  153. package/dist/types/noaa.js +5 -0
  154. package/dist/types/noaa.js.map +1 -0
  155. package/dist/types/nominatim.d.ts +72 -0
  156. package/dist/types/nominatim.d.ts.map +1 -0
  157. package/dist/types/nominatim.js +6 -0
  158. package/dist/types/nominatim.js.map +1 -0
  159. package/dist/types/openmeteo.d.ts +583 -0
  160. package/dist/types/openmeteo.d.ts.map +1 -0
  161. package/dist/types/openmeteo.js +6 -0
  162. package/dist/types/openmeteo.js.map +1 -0
  163. package/dist/types/savedLocations.d.ts +58 -0
  164. package/dist/types/savedLocations.d.ts.map +1 -0
  165. package/dist/types/savedLocations.js +5 -0
  166. package/dist/types/savedLocations.js.map +1 -0
  167. package/dist/types/wildfire.d.ts +83 -0
  168. package/dist/types/wildfire.d.ts.map +1 -0
  169. package/dist/types/wildfire.js +5 -0
  170. package/dist/types/wildfire.js.map +1 -0
  171. package/dist/utils/airQuality.d.ts +54 -0
  172. package/dist/utils/airQuality.d.ts.map +1 -0
  173. package/dist/utils/airQuality.js +251 -0
  174. package/dist/utils/airQuality.js.map +1 -0
  175. package/dist/utils/cache.d.ts +69 -0
  176. package/dist/utils/cache.d.ts.map +1 -0
  177. package/dist/utils/cache.js +164 -0
  178. package/dist/utils/cache.js.map +1 -0
  179. package/dist/utils/distance.d.ts +25 -0
  180. package/dist/utils/distance.d.ts.map +1 -0
  181. package/dist/utils/distance.js +40 -0
  182. package/dist/utils/distance.js.map +1 -0
  183. package/dist/utils/fireWeather.d.ts +76 -0
  184. package/dist/utils/fireWeather.d.ts.map +1 -0
  185. package/dist/utils/fireWeather.js +243 -0
  186. package/dist/utils/fireWeather.js.map +1 -0
  187. package/dist/utils/geography.d.ts +79 -0
  188. package/dist/utils/geography.d.ts.map +1 -0
  189. package/dist/utils/geography.js +266 -0
  190. package/dist/utils/geography.js.map +1 -0
  191. package/dist/utils/geohash.d.ts +62 -0
  192. package/dist/utils/geohash.d.ts.map +1 -0
  193. package/dist/utils/geohash.js +146 -0
  194. package/dist/utils/geohash.js.map +1 -0
  195. package/dist/utils/locationResolver.d.ts +34 -0
  196. package/dist/utils/locationResolver.d.ts.map +1 -0
  197. package/dist/utils/locationResolver.js +120 -0
  198. package/dist/utils/locationResolver.js.map +1 -0
  199. package/dist/utils/logger.d.ts +75 -0
  200. package/dist/utils/logger.d.ts.map +1 -0
  201. package/dist/utils/logger.js +153 -0
  202. package/dist/utils/logger.js.map +1 -0
  203. package/dist/utils/marine.d.ts +59 -0
  204. package/dist/utils/marine.d.ts.map +1 -0
  205. package/dist/utils/marine.js +215 -0
  206. package/dist/utils/marine.js.map +1 -0
  207. package/dist/utils/normals.d.ts +86 -0
  208. package/dist/utils/normals.d.ts.map +1 -0
  209. package/dist/utils/normals.js +223 -0
  210. package/dist/utils/normals.js.map +1 -0
  211. package/dist/utils/snow.d.ts +45 -0
  212. package/dist/utils/snow.d.ts.map +1 -0
  213. package/dist/utils/snow.js +144 -0
  214. package/dist/utils/snow.js.map +1 -0
  215. package/dist/utils/temperatureConversion.d.ts +12 -0
  216. package/dist/utils/temperatureConversion.d.ts.map +1 -0
  217. package/dist/utils/temperatureConversion.js +17 -0
  218. package/dist/utils/temperatureConversion.js.map +1 -0
  219. package/dist/utils/timezone.d.ts +56 -0
  220. package/dist/utils/timezone.d.ts.map +1 -0
  221. package/dist/utils/timezone.js +167 -0
  222. package/dist/utils/timezone.js.map +1 -0
  223. package/dist/utils/units.d.ts +69 -0
  224. package/dist/utils/units.d.ts.map +1 -0
  225. package/dist/utils/units.js +158 -0
  226. package/dist/utils/units.js.map +1 -0
  227. package/dist/utils/validation.d.ts +89 -0
  228. package/dist/utils/validation.d.ts.map +1 -0
  229. package/dist/utils/validation.js +177 -0
  230. package/dist/utils/validation.js.map +1 -0
  231. package/dist/utils/version.d.ts +15 -0
  232. package/dist/utils/version.d.ts.map +1 -0
  233. package/dist/utils/version.js +24 -0
  234. package/dist/utils/version.js.map +1 -0
  235. package/package.json +74 -0
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Structured logging utility for MCP server
3
+ *
4
+ * IMPORTANT: MCP servers use stdio for communication, so all logging MUST go to stderr
5
+ * (console.error) to avoid interfering with the MCP protocol on stdout.
6
+ */
7
+ export declare enum LogLevel {
8
+ DEBUG = 0,
9
+ INFO = 1,
10
+ WARN = 2,
11
+ ERROR = 3
12
+ }
13
+ export declare class Logger {
14
+ private level;
15
+ private context?;
16
+ constructor(level?: LogLevel, context?: string);
17
+ /**
18
+ * Create a child logger with a specific context
19
+ */
20
+ child(context: string): Logger;
21
+ /**
22
+ * Set the logging level
23
+ */
24
+ setLevel(level: LogLevel): void;
25
+ /**
26
+ * Internal logging method
27
+ */
28
+ private log;
29
+ /**
30
+ * Log debug message (detailed information for diagnosing problems)
31
+ */
32
+ debug(message: string, metadata?: Record<string, any>): void;
33
+ /**
34
+ * Log info message (general informational messages)
35
+ */
36
+ info(message: string, metadata?: Record<string, any>): void;
37
+ /**
38
+ * Log warning message (warning messages for potentially harmful situations)
39
+ */
40
+ warn(message: string, metadata?: Record<string, any>): void;
41
+ /**
42
+ * Log error message (error events that might still allow the application to continue)
43
+ */
44
+ error(message: string, error?: Error, metadata?: Record<string, any>): void;
45
+ /**
46
+ * Log API request
47
+ */
48
+ logApiRequest(service: string, endpoint: string, metadata?: Record<string, any>): void;
49
+ /**
50
+ * Log API response
51
+ */
52
+ logApiResponse(service: string, endpoint: string, success: boolean, duration?: number): void;
53
+ /**
54
+ * Log cache operation
55
+ */
56
+ logCacheOperation(operation: 'hit' | 'miss' | 'set' | 'evict', key: string): void;
57
+ }
58
+ export declare const logger: Logger;
59
+ /**
60
+ * Round coordinates for logging to protect user privacy
61
+ * Reduces precision to ~1.1km accuracy (2 decimal places)
62
+ * Set LOG_PII=true environment variable to log full precision (not recommended for production)
63
+ *
64
+ * Privacy rationale: Precise coordinates can reveal sensitive locations (homes, workplaces).
65
+ * Rounded coordinates provide sufficient context for debugging while protecting user privacy.
66
+ *
67
+ * @param latitude - Original latitude
68
+ * @param longitude - Original longitude
69
+ * @returns Rounded coordinates object
70
+ */
71
+ export declare function redactCoordinatesForLogging(latitude: number, longitude: number): {
72
+ lat: number;
73
+ lon: number;
74
+ };
75
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,oBAAY,QAAQ;IAClB,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;CACV;AAeD,qBAAa,MAAM;IACjB,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,OAAO,CAAC,CAAS;gBAEb,KAAK,GAAE,QAAwB,EAAE,OAAO,CAAC,EAAE,MAAM;IAK7D;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAI9B;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAI/B;;OAEG;IACH,OAAO,CAAC,GAAG;IA8BX;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAI5D;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAI3D;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAI3D;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAI3E;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAQtF;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAU5F;;OAEG;IACH,iBAAiB,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;CAGlF;AAkBD,eAAO,MAAM,MAAM,QAAwB,CAAC;AAE5C;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAc7G"}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Structured logging utility for MCP server
3
+ *
4
+ * IMPORTANT: MCP servers use stdio for communication, so all logging MUST go to stderr
5
+ * (console.error) to avoid interfering with the MCP protocol on stdout.
6
+ */
7
+ export var LogLevel;
8
+ (function (LogLevel) {
9
+ LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
10
+ LogLevel[LogLevel["INFO"] = 1] = "INFO";
11
+ LogLevel[LogLevel["WARN"] = 2] = "WARN";
12
+ LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
13
+ })(LogLevel || (LogLevel = {}));
14
+ export class Logger {
15
+ level;
16
+ context;
17
+ constructor(level = LogLevel.INFO, context) {
18
+ this.level = level;
19
+ this.context = context;
20
+ }
21
+ /**
22
+ * Create a child logger with a specific context
23
+ */
24
+ child(context) {
25
+ return new Logger(this.level, context);
26
+ }
27
+ /**
28
+ * Set the logging level
29
+ */
30
+ setLevel(level) {
31
+ this.level = level;
32
+ }
33
+ /**
34
+ * Internal logging method
35
+ */
36
+ log(level, message, metadata, error) {
37
+ // Skip if below current log level
38
+ if (level < this.level)
39
+ return;
40
+ const entry = {
41
+ timestamp: new Date().toISOString(),
42
+ level: LogLevel[level],
43
+ message,
44
+ };
45
+ if (this.context) {
46
+ entry.context = this.context;
47
+ }
48
+ if (error) {
49
+ entry.error = {
50
+ message: error.message,
51
+ stack: error.stack,
52
+ code: error.code,
53
+ };
54
+ }
55
+ if (metadata) {
56
+ entry.metadata = metadata;
57
+ }
58
+ // Output to stderr for MCP compatibility
59
+ console.error(JSON.stringify(entry));
60
+ }
61
+ /**
62
+ * Log debug message (detailed information for diagnosing problems)
63
+ */
64
+ debug(message, metadata) {
65
+ this.log(LogLevel.DEBUG, message, metadata);
66
+ }
67
+ /**
68
+ * Log info message (general informational messages)
69
+ */
70
+ info(message, metadata) {
71
+ this.log(LogLevel.INFO, message, metadata);
72
+ }
73
+ /**
74
+ * Log warning message (warning messages for potentially harmful situations)
75
+ */
76
+ warn(message, metadata) {
77
+ this.log(LogLevel.WARN, message, metadata);
78
+ }
79
+ /**
80
+ * Log error message (error events that might still allow the application to continue)
81
+ */
82
+ error(message, error, metadata) {
83
+ this.log(LogLevel.ERROR, message, metadata, error);
84
+ }
85
+ /**
86
+ * Log API request
87
+ */
88
+ logApiRequest(service, endpoint, metadata) {
89
+ this.debug(`API request to ${service}`, {
90
+ service,
91
+ endpoint,
92
+ ...metadata,
93
+ });
94
+ }
95
+ /**
96
+ * Log API response
97
+ */
98
+ logApiResponse(service, endpoint, success, duration) {
99
+ const level = success ? LogLevel.DEBUG : LogLevel.WARN;
100
+ this.log(level, `API response from ${service}`, {
101
+ service,
102
+ endpoint,
103
+ success,
104
+ duration,
105
+ });
106
+ }
107
+ /**
108
+ * Log cache operation
109
+ */
110
+ logCacheOperation(operation, key) {
111
+ this.debug(`Cache ${operation}`, { operation, key });
112
+ }
113
+ }
114
+ /**
115
+ * Create the default logger instance
116
+ * Level is controlled by LOG_LEVEL environment variable
117
+ */
118
+ function createDefaultLogger() {
119
+ const levelStr = process.env.LOG_LEVEL?.toUpperCase();
120
+ let level = LogLevel.INFO; // Default to INFO
121
+ if (levelStr && levelStr in LogLevel) {
122
+ level = LogLevel[levelStr];
123
+ }
124
+ return new Logger(level);
125
+ }
126
+ // Export singleton instance
127
+ export const logger = createDefaultLogger();
128
+ /**
129
+ * Round coordinates for logging to protect user privacy
130
+ * Reduces precision to ~1.1km accuracy (2 decimal places)
131
+ * Set LOG_PII=true environment variable to log full precision (not recommended for production)
132
+ *
133
+ * Privacy rationale: Precise coordinates can reveal sensitive locations (homes, workplaces).
134
+ * Rounded coordinates provide sufficient context for debugging while protecting user privacy.
135
+ *
136
+ * @param latitude - Original latitude
137
+ * @param longitude - Original longitude
138
+ * @returns Rounded coordinates object
139
+ */
140
+ export function redactCoordinatesForLogging(latitude, longitude) {
141
+ // Check if PII logging is explicitly enabled (not recommended)
142
+ const logPII = process.env.LOG_PII === 'true';
143
+ if (logPII) {
144
+ return { lat: latitude, lon: longitude };
145
+ }
146
+ // Round to 2 decimal places (~1.1km precision at equator)
147
+ // This balances GDPR/CPRA data minimization with operational observability
148
+ return {
149
+ lat: Math.round(latitude * 100) / 100,
150
+ lon: Math.round(longitude * 100) / 100
151
+ };
152
+ }
153
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Utility functions for marine conditions data formatting and interpretation
3
+ */
4
+ import type { GridpointResponse } from '../types/noaa.js';
5
+ /**
6
+ * NOAA Marine Conditions extracted from gridpoint data
7
+ */
8
+ export interface NOAAMarineConditions {
9
+ waveHeight?: number;
10
+ wavePeriod?: number;
11
+ waveDirection?: number;
12
+ windSpeed?: number;
13
+ windDirection?: number;
14
+ windGust?: number;
15
+ timestamp: string;
16
+ }
17
+ /**
18
+ * Extract marine conditions from NOAA gridpoint response
19
+ */
20
+ export declare function extractNOAAMarineConditions(gridpoint: GridpointResponse): NOAAMarineConditions | null;
21
+ /**
22
+ * Format wave height with appropriate units and precision
23
+ */
24
+ export declare function formatWaveHeight(meters: number | undefined): string;
25
+ /**
26
+ * Format wave period with units
27
+ */
28
+ export declare function formatWavePeriod(seconds: number | undefined): string;
29
+ /**
30
+ * Format wind speed with units (converts km/h to knots for marine)
31
+ */
32
+ export declare function formatWindSpeed(kmh: number | undefined): string;
33
+ /**
34
+ * Format ocean current velocity
35
+ */
36
+ export declare function formatCurrentVelocity(metersPerSecond: number | undefined): string;
37
+ /**
38
+ * Convert degrees to cardinal/ordinal direction
39
+ */
40
+ export declare function formatDirection(degrees: number | undefined): string;
41
+ /**
42
+ * Categorize wave height
43
+ */
44
+ export interface WaveHeightCategory {
45
+ description: string;
46
+ level: string;
47
+ recommendation: string;
48
+ }
49
+ export declare function getWaveHeightCategory(meters: number | undefined): WaveHeightCategory;
50
+ /**
51
+ * Overall safety assessment based on multiple factors
52
+ */
53
+ export interface SafetyAssessment {
54
+ level: string;
55
+ description: string;
56
+ recommendation: string;
57
+ }
58
+ export declare function getSafetyAssessment(totalWaveHeight: number | undefined, windWaveHeight: number | undefined, swellHeight: number | undefined, wavePeriod: number | undefined): SafetyAssessment;
59
+ //# sourceMappingURL=marine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marine.d.ts","sourceRoot":"","sources":["../../src/utils/marine.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAuB,MAAM,kBAAkB,CAAC;AAE/E;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAqBD;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,iBAAiB,GAAG,oBAAoB,GAAG,IAAI,CAyBrG;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAOnE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAMpE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAQ/D;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,eAAe,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAQjF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAmBnE;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,kBAAkB,CAiEpF;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CACjC,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,gBAAgB,CAoClB"}
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Utility functions for marine conditions data formatting and interpretation
3
+ */
4
+ /**
5
+ * Extract current value from a gridpoint data series
6
+ */
7
+ function extractCurrentValue(series) {
8
+ if (!series || !series.values || series.values.length === 0) {
9
+ return undefined;
10
+ }
11
+ // Find the first valid value (gridpoint data is time-ordered)
12
+ for (const entry of series.values) {
13
+ const value = entry.value;
14
+ if (value !== null && value !== undefined) {
15
+ return value;
16
+ }
17
+ }
18
+ return undefined;
19
+ }
20
+ /**
21
+ * Extract marine conditions from NOAA gridpoint response
22
+ */
23
+ export function extractNOAAMarineConditions(gridpoint) {
24
+ const props = gridpoint.properties;
25
+ // Extract marine data
26
+ const waveHeight = extractCurrentValue(props.waveHeight);
27
+ const wavePeriod = extractCurrentValue(props.wavePeriod);
28
+ const waveDirection = extractCurrentValue(props.waveDirection);
29
+ const windSpeed = extractCurrentValue(props.windSpeed);
30
+ const windDirection = extractCurrentValue(props.windDirection);
31
+ const windGust = extractCurrentValue(props.windGust);
32
+ // Check if we have any marine data
33
+ if (waveHeight === undefined && wavePeriod === undefined && waveDirection === undefined) {
34
+ return null;
35
+ }
36
+ return {
37
+ waveHeight,
38
+ wavePeriod,
39
+ waveDirection,
40
+ windSpeed,
41
+ windDirection,
42
+ windGust,
43
+ timestamp: props.updateTime
44
+ };
45
+ }
46
+ /**
47
+ * Format wave height with appropriate units and precision
48
+ */
49
+ export function formatWaveHeight(meters) {
50
+ if (meters === undefined || meters === null) {
51
+ return 'N/A';
52
+ }
53
+ const feet = meters * 3.28084;
54
+ return `${meters.toFixed(1)}m (${feet.toFixed(1)}ft)`;
55
+ }
56
+ /**
57
+ * Format wave period with units
58
+ */
59
+ export function formatWavePeriod(seconds) {
60
+ if (seconds === undefined || seconds === null) {
61
+ return 'N/A';
62
+ }
63
+ return `${seconds.toFixed(1)}s`;
64
+ }
65
+ /**
66
+ * Format wind speed with units (converts km/h to knots for marine)
67
+ */
68
+ export function formatWindSpeed(kmh) {
69
+ if (kmh === undefined || kmh === null) {
70
+ return 'N/A';
71
+ }
72
+ // Convert km/h to knots (1 km/h = 0.539957 knots)
73
+ const knots = kmh * 0.539957;
74
+ return `${knots.toFixed(1)} knots (${kmh.toFixed(1)} km/h)`;
75
+ }
76
+ /**
77
+ * Format ocean current velocity
78
+ */
79
+ export function formatCurrentVelocity(metersPerSecond) {
80
+ if (metersPerSecond === undefined || metersPerSecond === null) {
81
+ return 'N/A';
82
+ }
83
+ // Convert m/s to knots (1 m/s = 1.94384 knots)
84
+ const knots = metersPerSecond * 1.94384;
85
+ return `${metersPerSecond.toFixed(2)} m/s (${knots.toFixed(2)} knots)`;
86
+ }
87
+ /**
88
+ * Convert degrees to cardinal/ordinal direction
89
+ */
90
+ export function formatDirection(degrees) {
91
+ if (degrees === undefined || degrees === null) {
92
+ return 'N/A';
93
+ }
94
+ const directions = [
95
+ 'N', 'NNE', 'NE', 'ENE',
96
+ 'E', 'ESE', 'SE', 'SSE',
97
+ 'S', 'SSW', 'SW', 'WSW',
98
+ 'W', 'WNW', 'NW', 'NNW'
99
+ ];
100
+ // Normalize to 0-360
101
+ const normalized = ((degrees % 360) + 360) % 360;
102
+ // Calculate index (16 directions, each 22.5 degrees)
103
+ const index = Math.round(normalized / 22.5) % 16;
104
+ return `${directions[index]} (${Math.round(normalized)}°)`;
105
+ }
106
+ export function getWaveHeightCategory(meters) {
107
+ if (meters === undefined || meters === null) {
108
+ return {
109
+ description: 'Unknown',
110
+ level: 'Unknown',
111
+ recommendation: 'No data available'
112
+ };
113
+ }
114
+ // Based on WMO Sea State Code and Douglas Sea Scale
115
+ if (meters < 0.1) {
116
+ return {
117
+ description: 'Calm (glassy)',
118
+ level: 'Calm',
119
+ recommendation: 'Ideal for all water activities'
120
+ };
121
+ }
122
+ else if (meters < 0.5) {
123
+ return {
124
+ description: 'Calm (rippled)',
125
+ level: 'Calm',
126
+ recommendation: 'Excellent conditions for all vessels'
127
+ };
128
+ }
129
+ else if (meters < 1.25) {
130
+ return {
131
+ description: 'Smooth',
132
+ level: 'Slight',
133
+ recommendation: 'Good conditions for most activities'
134
+ };
135
+ }
136
+ else if (meters < 2.5) {
137
+ return {
138
+ description: 'Slight',
139
+ level: 'Moderate',
140
+ recommendation: 'Safe for experienced boaters'
141
+ };
142
+ }
143
+ else if (meters < 4.0) {
144
+ return {
145
+ description: 'Moderate',
146
+ level: 'Moderate',
147
+ recommendation: 'Use caution, especially for small craft'
148
+ };
149
+ }
150
+ else if (meters < 6.0) {
151
+ return {
152
+ description: 'Rough',
153
+ level: 'Rough',
154
+ recommendation: 'Hazardous for small vessels, secure all gear'
155
+ };
156
+ }
157
+ else if (meters < 9.0) {
158
+ return {
159
+ description: 'Very Rough',
160
+ level: 'Very Rough',
161
+ recommendation: 'Dangerous conditions, avoid non-essential travel'
162
+ };
163
+ }
164
+ else if (meters < 14.0) {
165
+ return {
166
+ description: 'High',
167
+ level: 'High',
168
+ recommendation: 'Very dangerous, only experienced vessels should be out'
169
+ };
170
+ }
171
+ else {
172
+ return {
173
+ description: 'Very High',
174
+ level: 'Extreme',
175
+ recommendation: 'Extremely dangerous, all vessels should seek shelter'
176
+ };
177
+ }
178
+ }
179
+ export function getSafetyAssessment(totalWaveHeight, windWaveHeight, swellHeight, wavePeriod) {
180
+ if (totalWaveHeight === undefined || totalWaveHeight === null) {
181
+ return {
182
+ level: 'Unknown',
183
+ description: 'Marine conditions data not available',
184
+ recommendation: 'Consult local marine forecast'
185
+ };
186
+ }
187
+ const waveCategory = getWaveHeightCategory(totalWaveHeight);
188
+ // Adjust based on wave period (short period = choppy/uncomfortable)
189
+ let adjustedDescription = waveCategory.description;
190
+ if (wavePeriod !== undefined && wavePeriod < 6 && totalWaveHeight > 1.0) {
191
+ adjustedDescription += ' and choppy (short period)';
192
+ }
193
+ else if (wavePeriod !== undefined && wavePeriod > 12 && totalWaveHeight > 2.0) {
194
+ adjustedDescription += ' with long-period swell (powerful)';
195
+ }
196
+ // Add context about wind vs swell
197
+ let context = '';
198
+ if (windWaveHeight !== undefined && swellHeight !== undefined) {
199
+ if (windWaveHeight > swellHeight * 1.5) {
200
+ context = ' Conditions dominated by local wind waves.';
201
+ }
202
+ else if (swellHeight > windWaveHeight * 1.5) {
203
+ context = ' Conditions dominated by swell from distant systems.';
204
+ }
205
+ else {
206
+ context = ' Mixed wind and swell conditions.';
207
+ }
208
+ }
209
+ return {
210
+ level: waveCategory.level,
211
+ description: adjustedDescription + context,
212
+ recommendation: waveCategory.recommendation
213
+ };
214
+ }
215
+ //# sourceMappingURL=marine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marine.js","sourceRoot":"","sources":["../../src/utils/marine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Utility functions for computing climate normals from historical data
3
+ *
4
+ * Climate normals are 30-year averages (1991-2020) used to provide
5
+ * context for current weather conditions.
6
+ *
7
+ * Hybrid Strategy:
8
+ * 1. Try NCEI (US only, requires token) for official normals
9
+ * 2. Fall back to Open-Meteo (global, no token) computed normals
10
+ */
11
+ import type { OpenMeteoHistoricalResponse, ClimateNormals } from '../types/openmeteo.js';
12
+ import type { OpenMeteoService } from '../services/openmeteo.js';
13
+ import type { NCEIService } from '../services/ncei.js';
14
+ /**
15
+ * Compute climate normals from 30 years of historical data
16
+ *
17
+ * @param historicalData - 30 years of daily historical weather data from Open-Meteo
18
+ * @param targetMonth - Month (1-12) to compute normals for
19
+ * @param targetDay - Day of month (1-31) to compute normals for
20
+ * @returns Climate normals (30-year averages) for the specified date
21
+ */
22
+ export declare function computeNormalsFrom30YearData(historicalData: OpenMeteoHistoricalResponse, targetMonth: number, targetDay: number): ClimateNormals;
23
+ /**
24
+ * Generate a cache key for climate normals
25
+ *
26
+ * @param latitude - Latitude (rounded to 2 decimals)
27
+ * @param longitude - Longitude (rounded to 2 decimals)
28
+ * @param month - Month (1-12)
29
+ * @param day - Day of month (1-31)
30
+ * @returns Cache key string
31
+ */
32
+ export declare function getNormalsCacheKey(latitude: number, longitude: number, month: number, day: number): string;
33
+ /**
34
+ * Calculate departure from normal
35
+ *
36
+ * @param actual - Actual temperature value
37
+ * @param normal - Normal (average) temperature value
38
+ * @returns Departure with sign (e.g., +10, -5)
39
+ */
40
+ export declare function calculateDeparture(actual: number, normal: number): string;
41
+ /**
42
+ * Format climate normals for display
43
+ *
44
+ * @param normals - Climate normals data
45
+ * @param currentTemp - Optional current temperature for comparison
46
+ * @returns Formatted markdown string
47
+ */
48
+ export declare function formatNormals(normals: ClimateNormals, currentTemp?: {
49
+ high?: number;
50
+ low?: number;
51
+ }): string;
52
+ /**
53
+ * Get date components from Date object or ISO string
54
+ *
55
+ * @param date - Date object or ISO string
56
+ * @returns Object with month (1-12) and day (1-31)
57
+ */
58
+ export declare function getDateComponents(date: Date | string): {
59
+ month: number;
60
+ day: number;
61
+ };
62
+ /**
63
+ * Check if coordinates are within the contiguous United States
64
+ *
65
+ * @param latitude - Latitude
66
+ * @param longitude - Longitude
67
+ * @returns true if location is in contiguous US
68
+ */
69
+ export declare function isLocationInUS(latitude: number, longitude: number): boolean;
70
+ /**
71
+ * Get climate normals using hybrid strategy
72
+ *
73
+ * Strategy:
74
+ * 1. If location is in US and NCEI token available, try NCEI first
75
+ * 2. Fall back to Open-Meteo computed normals (always works)
76
+ *
77
+ * @param openMeteoService - Open-Meteo service instance
78
+ * @param nceiService - NCEI service instance (optional)
79
+ * @param latitude - Latitude
80
+ * @param longitude - Longitude
81
+ * @param month - Month (1-12)
82
+ * @param day - Day of month (1-31)
83
+ * @returns Climate normals with source indication
84
+ */
85
+ export declare function getClimateNormals(openMeteoService: OpenMeteoService, nceiService: NCEIService | undefined, latitude: number, longitude: number, month: number, day: number): Promise<ClimateNormals>;
86
+ //# sourceMappingURL=normals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normals.d.ts","sourceRoot":"","sources":["../../src/utils/normals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,2BAA2B,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEzF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAUvD;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAC1C,cAAc,EAAE,2BAA2B,EAC3C,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,cAAc,CAsDhB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,GACV,MAAM,CAKR;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGzE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,cAAc,EACvB,WAAW,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,MAAM,CAkCR;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAMrF;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAU3E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,iBAAiB,CACrC,gBAAgB,EAAE,gBAAgB,EAClC,WAAW,EAAE,WAAW,GAAG,SAAS,EACpC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,cAAc,CAAC,CA8CzB"}