local-risk-alert-feed 0.2.2 → 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.
- package/README.md +358 -0
- package/package.json +1 -1
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.
|
|
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",
|