local-risk-alert-feed 0.2.1 → 0.2.3

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 (2) hide show
  1. package/README.md +358 -0
  2. package/package.json +1 -17
package/README.md ADDED
@@ -0,0 +1,358 @@
1
+ # local-risk-alert-feed
2
+
3
+ A TypeScript library for aggregating local risk alerts from multiple data sources via a plugin system. Designed for SaaS and serverless applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install local-risk-alert-feed
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import {
15
+ AlertFeed,
16
+ NWSWeatherPlugin,
17
+ PhoenixFirePlugin,
18
+ NIFCWildfirePlugin,
19
+ ArizonaTrafficPlugin,
20
+ } from 'local-risk-alert-feed';
21
+
22
+ // Create the feed
23
+ const feed = new AlertFeed({
24
+ continueOnPluginError: true,
25
+ pluginTimeoutMs: 30000,
26
+ });
27
+
28
+ // Register plugins
29
+ await feed.registerPlugins([
30
+ { plugin: new NWSWeatherPlugin() },
31
+ { plugin: new PhoenixFirePlugin({ includeEMS: true }) },
32
+ { plugin: new NIFCWildfirePlugin() },
33
+ { plugin: new ArizonaTrafficPlugin() },
34
+ ]);
35
+
36
+ // Query for alerts near a location
37
+ const response = await feed.query({
38
+ location: { latitude: 33.4484, longitude: -112.074 },
39
+ timeRange: 'past-7d',
40
+ radiusMeters: 2000,
41
+ limit: 100,
42
+ });
43
+
44
+ console.log(`Found ${response.alerts.length} alerts`);
45
+
46
+ // Cleanup when done
47
+ await feed.dispose();
48
+ ```
49
+
50
+ ## Available Plugins
51
+
52
+ | Plugin | Coverage | Categories | Data Freshness |
53
+ |--------|----------|------------|----------------|
54
+ | `NWSWeatherPlugin` | United States | weather | Real-time |
55
+ | `PhoenixFirePlugin` | Phoenix metro | fire, medical | ~1-2 days |
56
+ | `NIFCWildfirePlugin` | United States | fire | Real-time |
57
+ | `ArizonaTrafficPlugin` | Arizona | traffic | Real-time |
58
+ | `PhoenixEventsPlugin` | Phoenix area | event | Real-time |
59
+ | `PhoenixConventionCenterPlugin` | Downtown Phoenix | event | Real-time |
60
+ | `AirNowPlugin` | United States | air-quality | Real-time |
61
+
62
+ ## Plugin Configuration
63
+
64
+ ### NWSWeatherPlugin
65
+
66
+ National Weather Service alerts for the United States.
67
+
68
+ ```typescript
69
+ new NWSWeatherPlugin({
70
+ cacheTtlMs: 300000, // 5 minute cache (default)
71
+ });
72
+ ```
73
+
74
+ ### PhoenixFirePlugin
75
+
76
+ Fire and EMS incidents from Phoenix Fire Department (ArcGIS 30-day history).
77
+
78
+ ```typescript
79
+ new PhoenixFirePlugin({
80
+ includeEMS: true, // Include medical calls (default: true)
81
+ includeService: false, // Include non-emergency service calls (default: false)
82
+ limit: 500, // Max records per request (default: 500)
83
+ });
84
+ ```
85
+
86
+ ### NIFCWildfirePlugin
87
+
88
+ Active wildfires from National Interagency Fire Center.
89
+
90
+ ```typescript
91
+ new NIFCWildfirePlugin({
92
+ includePrescribedBurns: false, // Include RX burns (default: false)
93
+ minAcres: 0, // Minimum fire size (default: 0)
94
+ states: ['AZ', 'CA', 'NV'], // Filter by states (default: all)
95
+ });
96
+ ```
97
+
98
+ ### PhoenixEventsPlugin
99
+
100
+ Events from Ticketmaster Discovery API.
101
+
102
+ ```typescript
103
+ new PhoenixEventsPlugin({
104
+ ticketmasterApiKey: process.env.TICKETMASTER_API_KEY,
105
+ limit: 100,
106
+ });
107
+ ```
108
+
109
+ ### AirNowPlugin
110
+
111
+ Air quality data from EPA AirNow.
112
+
113
+ ```typescript
114
+ new AirNowPlugin({
115
+ apiKey: process.env.AIRNOW_API_KEY, // Required
116
+ });
117
+ ```
118
+
119
+ ## Cache Providers
120
+
121
+ The library uses a **bring-your-own-client** pattern for caching. No external dependencies are required.
122
+
123
+ ### In-Memory (Default)
124
+
125
+ Zero dependencies, suitable for development or single-instance deployments.
126
+
127
+ ```typescript
128
+ import { AlertFeed, InMemoryCacheProvider } from 'local-risk-alert-feed';
129
+
130
+ const feed = new AlertFeed({
131
+ cacheProvider: new InMemoryCacheProvider(),
132
+ });
133
+ ```
134
+
135
+ ### Vercel KV
136
+
137
+ Pass your own `@vercel/kv` instance (any version).
138
+
139
+ ```typescript
140
+ import { kv } from '@vercel/kv';
141
+ import { AlertFeed, VercelKVCacheProvider } from 'local-risk-alert-feed';
142
+
143
+ const feed = new AlertFeed({
144
+ cacheProvider: new VercelKVCacheProvider(kv, 'my-prefix:'),
145
+ });
146
+ ```
147
+
148
+ ### DynamoDB
149
+
150
+ Pass your own AWS SDK DynamoDB Document Client (any v3 version).
151
+
152
+ ```typescript
153
+ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
154
+ import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
155
+ import { AlertFeed, DynamoDBCacheProvider } from 'local-risk-alert-feed';
156
+
157
+ const client = new DynamoDBClient({});
158
+ const docClient = DynamoDBDocumentClient.from(client);
159
+
160
+ const feed = new AlertFeed({
161
+ cacheProvider: new DynamoDBCacheProvider(docClient, {
162
+ tableName: 'alert-cache',
163
+ keyAttribute: 'pk', // optional, default: 'pk'
164
+ valueAttribute: 'value', // optional, default: 'value'
165
+ ttlAttribute: 'ttl', // optional, default: 'ttl'
166
+ }),
167
+ });
168
+ ```
169
+
170
+ ### Custom Cache Provider
171
+
172
+ Implement the `CacheProvider` interface for any backend.
173
+
174
+ ```typescript
175
+ import { CacheProvider, AlertFeed } from 'local-risk-alert-feed';
176
+
177
+ const customCache: CacheProvider = {
178
+ async get<T>(key: string): Promise<T | null> {
179
+ // Your implementation
180
+ },
181
+ async set<T>(key: string, value: T, ttlMs?: number): Promise<void> {
182
+ // Your implementation
183
+ },
184
+ async delete(key: string): Promise<void> {
185
+ // Your implementation
186
+ },
187
+ async has(key: string): Promise<boolean> {
188
+ // Your implementation
189
+ },
190
+ };
191
+
192
+ const feed = new AlertFeed({ cacheProvider: customCache });
193
+ ```
194
+
195
+ ## Query Options
196
+
197
+ ```typescript
198
+ const response = await feed.query({
199
+ // Required
200
+ location: { latitude: 33.4484, longitude: -112.074 },
201
+
202
+ // Time range - preset string or explicit range
203
+ timeRange: 'past-7d', // or 'past-24h', 'past-1h', 'next-7d', etc.
204
+ // timeRange: { start: '2024-01-01T00:00:00Z', end: '2024-01-07T00:00:00Z' },
205
+
206
+ // Optional filters
207
+ radiusMeters: 2000, // Default: 10000 (10km)
208
+ categories: ['fire', 'weather'], // Filter by category
209
+ plugins: ['nws-weather'], // Filter by plugin ID
210
+ limit: 100, // Max alerts to return
211
+
212
+ // Include per-plugin timing/status info
213
+ includePluginResults: true,
214
+ });
215
+ ```
216
+
217
+ ## Response Format
218
+
219
+ ```typescript
220
+ interface AlertFeedResponse {
221
+ alerts: Alert[];
222
+ meta: {
223
+ queriedAt: string;
224
+ timeRange: { start: string; end: string };
225
+ location: { latitude: number; longitude: number };
226
+ radiusMeters: number;
227
+ totalCount: number;
228
+ truncated: boolean;
229
+ };
230
+ pluginResults?: PluginResult[]; // If includePluginResults: true
231
+ }
232
+
233
+ interface Alert {
234
+ id: string;
235
+ title: string;
236
+ description: string;
237
+ category: 'weather' | 'fire' | 'medical' | 'traffic' | 'event' | 'air-quality' | ...;
238
+ riskLevel: 'low' | 'moderate' | 'high' | 'severe' | 'extreme';
239
+ priority: number; // 1-5, higher = more urgent
240
+ temporalType: 'real-time' | 'scheduled' | 'historical';
241
+ location: {
242
+ point: { latitude: number; longitude: number };
243
+ address?: string;
244
+ city?: string;
245
+ state?: string;
246
+ };
247
+ timestamps: {
248
+ issued: string;
249
+ eventStart?: string;
250
+ eventEnd?: string;
251
+ expires?: string;
252
+ };
253
+ source: {
254
+ id: string;
255
+ name: string;
256
+ };
257
+ url?: string;
258
+ metadata?: Record<string, unknown>;
259
+ }
260
+ ```
261
+
262
+ ## Serverless Adapters
263
+
264
+ ### Vercel Edge/Serverless
265
+
266
+ ```typescript
267
+ import { createVercelHandler } from 'local-risk-alert-feed/adapters/vercel';
268
+ import { NWSWeatherPlugin, PhoenixFirePlugin } from 'local-risk-alert-feed';
269
+
270
+ export default createVercelHandler({
271
+ plugins: [
272
+ new NWSWeatherPlugin(),
273
+ new PhoenixFirePlugin(),
274
+ ],
275
+ // Optional: custom cache provider
276
+ // cacheProvider: new VercelKVCacheProvider(kv),
277
+ });
278
+ ```
279
+
280
+ ### AWS Lambda
281
+
282
+ ```typescript
283
+ import { createLambdaHandler } from 'local-risk-alert-feed/adapters/lambda';
284
+ import { NWSWeatherPlugin, PhoenixFirePlugin } from 'local-risk-alert-feed';
285
+
286
+ export const handler = createLambdaHandler({
287
+ plugins: [
288
+ new NWSWeatherPlugin(),
289
+ new PhoenixFirePlugin(),
290
+ ],
291
+ });
292
+ ```
293
+
294
+ ## Creating Custom Plugins
295
+
296
+ Extend `BasePlugin` to create your own data source plugins.
297
+
298
+ ```typescript
299
+ import { BasePlugin, PluginMetadata, PluginFetchOptions, PluginFetchResult } from 'local-risk-alert-feed';
300
+
301
+ export class MyCustomPlugin extends BasePlugin {
302
+ readonly metadata: PluginMetadata = {
303
+ id: 'my-custom-plugin',
304
+ name: 'My Custom Data Source',
305
+ version: '1.0.0',
306
+ description: 'Fetches alerts from my custom API',
307
+ coverage: {
308
+ type: 'regional',
309
+ center: { latitude: 40.7128, longitude: -74.0060 },
310
+ radiusMeters: 50000,
311
+ description: 'New York City area',
312
+ },
313
+ supportedTemporalTypes: ['real-time'],
314
+ supportedCategories: ['custom'],
315
+ refreshIntervalMs: 60000,
316
+ };
317
+
318
+ async fetchAlerts(options: PluginFetchOptions): Promise<PluginFetchResult> {
319
+ const { location, timeRange, radiusMeters } = options;
320
+
321
+ // Fetch from your API
322
+ const data = await this.fetchJson<MyApiResponse>('https://my-api.com/alerts');
323
+
324
+ // Transform to Alert format
325
+ const alerts = data.items.map(item => this.createAlert({
326
+ id: `my-plugin-${item.id}`,
327
+ title: item.title,
328
+ description: item.description,
329
+ riskLevel: 'moderate',
330
+ priority: 3,
331
+ category: 'custom',
332
+ temporalType: 'real-time',
333
+ location: {
334
+ point: { latitude: item.lat, longitude: item.lng },
335
+ },
336
+ timestamps: {
337
+ issued: item.createdAt,
338
+ },
339
+ }));
340
+
341
+ return { alerts, fromCache: false };
342
+ }
343
+ }
344
+ ```
345
+
346
+ ## Environment Variables
347
+
348
+ ```bash
349
+ # Optional - for Ticketmaster events
350
+ TICKETMASTER_API_KEY=your_key_here
351
+
352
+ # Optional - for AirNow air quality
353
+ AIRNOW_API_KEY=your_key_here
354
+ ```
355
+
356
+ ## License
357
+
358
+ UNLICENSED - Private package
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "local-risk-alert-feed",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "TypeScript library for aggregating local risk alerts from multiple data sources via a plugin system",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -79,22 +79,6 @@
79
79
  "dependencies": {
80
80
  "zod": "^3.25.76"
81
81
  },
82
- "peerDependencies": {
83
- "@aws-sdk/client-dynamodb": "^3.0.0",
84
- "@aws-sdk/lib-dynamodb": "^3.0.0",
85
- "@vercel/kv": "^1.0.0"
86
- },
87
- "peerDependenciesMeta": {
88
- "@aws-sdk/client-dynamodb": {
89
- "optional": true
90
- },
91
- "@aws-sdk/lib-dynamodb": {
92
- "optional": true
93
- },
94
- "@vercel/kv": {
95
- "optional": true
96
- }
97
- },
98
82
  "devDependencies": {
99
83
  "@types/aws-lambda": "^8.10.131",
100
84
  "@types/node": "^20.19.30",