@fedpulse/sdk 1.0.5 → 1.0.6
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 +360 -360
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,360 +1,360 @@
|
|
|
1
|
-
# @fedpulse/sdk
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/@fedpulse/sdk)
|
|
4
|
-
[](https://nodejs.org)
|
|
5
|
-
[](https://opensource.org/licenses/MIT)
|
|
6
|
-
|
|
7
|
-
Official JavaScript / TypeScript SDK for the [FedPulse API](https://fedpulse.dev). Search federal contract opportunities, check vendor exclusions, retrieve entity registrations, and access market intelligence — all with zero runtime dependencies.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Features
|
|
12
|
-
|
|
13
|
-
- **Typed end-to-end** — full TypeScript definitions for every request and response
|
|
14
|
-
- **Dual ESM + CJS** — works in Node.js (ESM / CommonJS), Deno, Bun, and bundlers
|
|
15
|
-
- **Zero runtime dependencies** — only uses Node.js built-ins (`fetch`, `crypto`)
|
|
16
|
-
- **Automatic retry** — exponential back-off with full jitter for 5xx / network errors
|
|
17
|
-
- **In-memory LRU cache** — 256-entry, 60 s TTL on all GET requests
|
|
18
|
-
- **Webhook verification** — HMAC-SHA256 signature + replay-attack protection
|
|
19
|
-
- **Cursor + offset pagination** — async generator helpers for all list endpoints
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## Requirements
|
|
24
|
-
|
|
25
|
-
- Node.js **≥ 18.0.0** (native `fetch` + `crypto`)
|
|
26
|
-
- A FedPulse API key — generate one at [app.fedpulse.dev/dashboard](https://app.fedpulse.dev/dashboard)
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## Installation
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
npm install @fedpulse/sdk
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
## Quick start
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
import { FedPulse } from '@fedpulse/sdk';
|
|
42
|
-
|
|
43
|
-
const client = new FedPulse({ apiKey: process.env.FEDPULSE_API_KEY! });
|
|
44
|
-
|
|
45
|
-
// Search opportunities
|
|
46
|
-
const result = await client.opportunities.list({
|
|
47
|
-
q: 'cybersecurity',
|
|
48
|
-
naics: ['541519', '541512'],
|
|
49
|
-
postedAfter: '2025-01-01',
|
|
50
|
-
limit: 25,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
console.log(result.data); // typed OpportunitySummary[]
|
|
54
|
-
console.log(result.pagination); // { total, page, limit, … }
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
## Authentication
|
|
60
|
-
|
|
61
|
-
Pass your API key to the constructor. It is sent as `Authorization: Bearer <key>` on every request.
|
|
62
|
-
|
|
63
|
-
```ts
|
|
64
|
-
const client = new FedPulse({ apiKey: 'fp_live_…' });
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Never hard-code keys in source — use environment variables.
|
|
68
|
-
|
|
69
|
-
---
|
|
70
|
-
|
|
71
|
-
## Resources
|
|
72
|
-
|
|
73
|
-
### `client.opportunities`
|
|
74
|
-
|
|
75
|
-
```ts
|
|
76
|
-
// List / search opportunities
|
|
77
|
-
await client.opportunities.list({ q: 'cloud', naics: ['541512'] });
|
|
78
|
-
|
|
79
|
-
// Retrieve a single opportunity
|
|
80
|
-
await client.opportunities.get('abc123-notice-id');
|
|
81
|
-
|
|
82
|
-
// Aggregate statistics
|
|
83
|
-
await client.opportunities.stats({ postedAfter: '2025-01-01' });
|
|
84
|
-
|
|
85
|
-
// Request a data export (async job)
|
|
86
|
-
await client.opportunities.createExport({ format: 'csv',
|
|
87
|
-
|
|
88
|
-
// Async generator — walks all pages automatically
|
|
89
|
-
for await (const page of client.opportunities.paginate({
|
|
90
|
-
console.log(page.data);
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### `client.exclusions`
|
|
95
|
-
|
|
96
|
-
```ts
|
|
97
|
-
// List all current exclusions
|
|
98
|
-
await client.exclusions.list({
|
|
99
|
-
|
|
100
|
-
// Retrieve one record
|
|
101
|
-
await client.exclusions.get('exclusion-id');
|
|
102
|
-
|
|
103
|
-
// Bulk check up to 100 entities
|
|
104
|
-
await client.exclusions.check({
|
|
105
|
-
entities: [
|
|
106
|
-
{ uei: 'ABC123DEF456' },
|
|
107
|
-
{ cage: '1A2B3' },
|
|
108
|
-
{ name: 'Acme Corp' },
|
|
109
|
-
],
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Stats
|
|
113
|
-
await client.exclusions.stats();
|
|
114
|
-
|
|
115
|
-
// Page through all exclusions
|
|
116
|
-
for await (const page of client.exclusions.paginate()) {
|
|
117
|
-
console.log(page.data);
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### `client.entities`
|
|
122
|
-
|
|
123
|
-
```ts
|
|
124
|
-
// List registered entities
|
|
125
|
-
await client.entities.list({ naics: ['336411'], state: 'VA' });
|
|
126
|
-
|
|
127
|
-
// Entity by UEI
|
|
128
|
-
await client.entities.get('ABCDEF123456');
|
|
129
|
-
|
|
130
|
-
// Contracts awarded to an entity
|
|
131
|
-
await client.entities.opportunities('ABCDEF123456', { limit: 50 });
|
|
132
|
-
|
|
133
|
-
// Exclusion check (never cached)
|
|
134
|
-
await client.entities.exclusionCheck('ABCDEF123456');
|
|
135
|
-
|
|
136
|
-
// Stats
|
|
137
|
-
await client.entities.stats();
|
|
138
|
-
|
|
139
|
-
// All pages
|
|
140
|
-
for await (const page of client.entities.paginate({ state: 'TX' })) {
|
|
141
|
-
console.log(page.data);
|
|
142
|
-
}
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### `client.intelligence`
|
|
146
|
-
|
|
147
|
-
```ts
|
|
148
|
-
// 360° entity intelligence profile
|
|
149
|
-
await client.intelligence.entityProfile('ABCDEF123456');
|
|
150
|
-
|
|
151
|
-
// Market analysis for a NAICS code
|
|
152
|
-
await client.intelligence.marketAnalysis('541512');
|
|
153
|
-
|
|
154
|
-
// Single-entity compliance check
|
|
155
|
-
await client.intelligence.complianceCheck({ uei: 'ABCDEF123456' });
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### `client.assistance`
|
|
159
|
-
|
|
160
|
-
Federal grants and assistance listings (CFDA programs).
|
|
161
|
-
|
|
162
|
-
```ts
|
|
163
|
-
// Search listings
|
|
164
|
-
await client.assistance.list({
|
|
165
|
-
|
|
166
|
-
// Retrieve one by CFDA program number (format: "XX.XXX")
|
|
167
|
-
await client.assistance.get('10.001');
|
|
168
|
-
|
|
169
|
-
// Aggregated stats
|
|
170
|
-
await client.assistance.stats();
|
|
171
|
-
|
|
172
|
-
// Auto-paginate all listings
|
|
173
|
-
for await (const page of client.assistance.paginate({ agency: 'HHS' })) {
|
|
174
|
-
console.log(page.data);
|
|
175
|
-
}
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### `client.analytics`
|
|
179
|
-
|
|
180
|
-
Per-user API usage analytics — scoped to the authenticated key.
|
|
181
|
-
|
|
182
|
-
```ts
|
|
183
|
-
// KPI totals (requests today, this month, error rates)
|
|
184
|
-
await client.analytics.summary({ range: '30d' });
|
|
185
|
-
|
|
186
|
-
// Per-day breakdown for charting (up to 90 days)
|
|
187
|
-
await client.analytics.chart({ range: '30d' });
|
|
188
|
-
|
|
189
|
-
// Top endpoints by request volume and P95 latency
|
|
190
|
-
await client.analytics.endpoints({ range: '7d' });
|
|
191
|
-
|
|
192
|
-
// Paginated raw request log
|
|
193
|
-
await client.analytics.logs({ limit: 20 });
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### `client.webhooks`
|
|
197
|
-
|
|
198
|
-
```ts
|
|
199
|
-
// List configured webhooks
|
|
200
|
-
await client.webhooks.list();
|
|
201
|
-
|
|
202
|
-
// Get a single webhook
|
|
203
|
-
await client.webhooks.get('
|
|
204
|
-
|
|
205
|
-
// Create
|
|
206
|
-
await client.webhooks.create({ url: 'https://example.com/hook', events: ['opportunity.new'] });
|
|
207
|
-
|
|
208
|
-
// Update / delete
|
|
209
|
-
await client.webhooks.update('
|
|
210
|
-
await client.webhooks.delete('
|
|
211
|
-
|
|
212
|
-
// Send a test event to the endpoint immediately
|
|
213
|
-
await client.webhooks.test('
|
|
214
|
-
|
|
215
|
-
// Resume a paused webhook (auto-paused after 5 consecutive failures)
|
|
216
|
-
await client.webhooks.resume('
|
|
217
|
-
|
|
218
|
-
// Deliveries log
|
|
219
|
-
await client.webhooks.listDeliveries('
|
|
220
|
-
await client.webhooks.getDelivery('
|
|
221
|
-
|
|
222
|
-
// Auto-paginate all delivery history
|
|
223
|
-
for await (const page of client.webhooks.deliveryPages('
|
|
224
|
-
console.log(page.data);
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
---
|
|
229
|
-
|
|
230
|
-
## Webhook signature verification
|
|
231
|
-
|
|
232
|
-
```ts
|
|
233
|
-
import { FedPulse } from '@fedpulse/sdk';
|
|
234
|
-
|
|
235
|
-
// In your Express / Fastify handler:
|
|
236
|
-
app.post('/webhooks', (req, res) => {
|
|
237
|
-
const { signatureHeader, timestampHeader } = FedPulse.extractWebhookHeaders(req.headers);
|
|
238
|
-
|
|
239
|
-
let event;
|
|
240
|
-
try {
|
|
241
|
-
event = FedPulse.verifyWebhook({
|
|
242
|
-
rawBody: req.body, // Buffer or string — the raw unparsed body
|
|
243
|
-
signatureHeader,
|
|
244
|
-
timestampHeader,
|
|
245
|
-
secret: process.env.FEDPULSE_WEBHOOK_SECRET!,
|
|
246
|
-
options: { maxAgeSeconds: 300 },
|
|
247
|
-
});
|
|
248
|
-
} catch (err) {
|
|
249
|
-
return res.status(400).send('Invalid signature');
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
console.log(event.event, event.data);
|
|
253
|
-
res.sendStatus(200);
|
|
254
|
-
});
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
---
|
|
258
|
-
|
|
259
|
-
## Pagination helpers
|
|
260
|
-
|
|
261
|
-
Every list resource exposes a `paginate()` async generator that walks all pages for you:
|
|
262
|
-
|
|
263
|
-
```ts
|
|
264
|
-
const allOpps: OpportunitySummary[] = [];
|
|
265
|
-
|
|
266
|
-
for await (const page of client.opportunities.paginate({
|
|
267
|
-
allOpps.push(...page.data);
|
|
268
|
-
}
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
---
|
|
272
|
-
|
|
273
|
-
## Error handling
|
|
274
|
-
|
|
275
|
-
All errors extend `FedPulseError`. Import specific classes for precise handling:
|
|
276
|
-
|
|
277
|
-
```ts
|
|
278
|
-
import {
|
|
279
|
-
FedPulse,
|
|
280
|
-
AuthenticationError,
|
|
281
|
-
PermissionError,
|
|
282
|
-
NotFoundError,
|
|
283
|
-
ValidationError,
|
|
284
|
-
RateLimitError,
|
|
285
|
-
ServerError,
|
|
286
|
-
NetworkError,
|
|
287
|
-
TimeoutError,
|
|
288
|
-
RetryExhaustedError,
|
|
289
|
-
} from '@fedpulse/sdk';
|
|
290
|
-
|
|
291
|
-
try {
|
|
292
|
-
await client.opportunities.get('bad-id');
|
|
293
|
-
} catch (err) {
|
|
294
|
-
if (err instanceof NotFoundError) {
|
|
295
|
-
console.log('Not found');
|
|
296
|
-
} else if (err instanceof RateLimitError) {
|
|
297
|
-
console.log(`Rate limited. Retry after: ${err.retryAfter}s`);
|
|
298
|
-
} else if (err instanceof RetryExhaustedError) {
|
|
299
|
-
console.log(`Failed after ${err.attempts} attempts`, err.lastError);
|
|
300
|
-
} else if (err instanceof NetworkError) {
|
|
301
|
-
console.log('Network issue — check internet connection');
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
| Class | Status | When thrown |
|
|
307
|
-
|---|---|---|
|
|
308
|
-
| `AuthenticationError` | 401 | Invalid or missing API key |
|
|
309
|
-
| `PermissionError` | 403 | Key lacks required permission |
|
|
310
|
-
| `NotFoundError` | 404 | Resource does not exist |
|
|
311
|
-
| `ValidationError` | 400/422 | Bad request parameters |
|
|
312
|
-
| `RateLimitError` | 429 | Too many requests (check `retryAfter`) |
|
|
313
|
-
| `ServerError` | 5xx | FedPulse server error |
|
|
314
|
-
| `NetworkError` | — | DNS failure, ECONNREFUSED, etc. |
|
|
315
|
-
| `TimeoutError` | — | Request exceeded `timeoutMs` |
|
|
316
|
-
| `RetryExhaustedError` | — | All retry attempts failed |
|
|
317
|
-
|
|
318
|
-
---
|
|
319
|
-
|
|
320
|
-
## Configuration
|
|
321
|
-
|
|
322
|
-
```ts
|
|
323
|
-
const client = new FedPulse({
|
|
324
|
-
apiKey: 'fp_live_…', // required
|
|
325
|
-
baseUrl: 'https://api.fedpulse.dev', // optional override
|
|
326
|
-
timeoutMs: 30_000, // per-attempt timeout (default: 30 s)
|
|
327
|
-
maxRetries: 3, // retries for 5xx / network errors (default: 3)
|
|
328
|
-
cacheSize: 256, // LRU cache entries, 0 to disable (default: 256)
|
|
329
|
-
cacheTtlMs: 60_000, // cache TTL in ms (default: 60 s)
|
|
330
|
-
});
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
### Rate limit info
|
|
334
|
-
|
|
335
|
-
```ts
|
|
336
|
-
const { data } = await client.opportunities.list({
|
|
337
|
-
console.log(client.rateLimit);
|
|
338
|
-
// { limit: 1000, remaining: 987, reset: 1740000000 }
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
### Clear cache
|
|
342
|
-
|
|
343
|
-
```ts
|
|
344
|
-
client.clearCache();
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
---
|
|
348
|
-
|
|
349
|
-
## CommonJS usage
|
|
350
|
-
|
|
351
|
-
```js
|
|
352
|
-
const { FedPulse } = require('@fedpulse/sdk');
|
|
353
|
-
const client = new FedPulse({ apiKey: process.env.FEDPULSE_API_KEY });
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
---
|
|
357
|
-
|
|
358
|
-
## License
|
|
359
|
-
|
|
360
|
-
MIT © [FedPulse](https://fedpulse.dev)
|
|
1
|
+
# @fedpulse/sdk
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@fedpulse/sdk)
|
|
4
|
+
[](https://nodejs.org)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
Official JavaScript / TypeScript SDK for the [FedPulse API](https://fedpulse.dev). Search federal contract opportunities, check vendor exclusions, retrieve entity registrations, and access market intelligence — all with zero runtime dependencies.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Typed end-to-end** — full TypeScript definitions for every request and response
|
|
14
|
+
- **Dual ESM + CJS** — works in Node.js (ESM / CommonJS), Deno, Bun, and bundlers
|
|
15
|
+
- **Zero runtime dependencies** — only uses Node.js built-ins (`fetch`, `crypto`)
|
|
16
|
+
- **Automatic retry** — exponential back-off with full jitter for 5xx / network errors
|
|
17
|
+
- **In-memory LRU cache** — 256-entry, 60 s TTL on all GET requests
|
|
18
|
+
- **Webhook verification** — HMAC-SHA256 signature + replay-attack protection
|
|
19
|
+
- **Cursor + offset pagination** — async generator helpers for all list endpoints
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Requirements
|
|
24
|
+
|
|
25
|
+
- Node.js **≥ 18.0.0** (native `fetch` + `crypto`)
|
|
26
|
+
- A FedPulse API key — generate one at [app.fedpulse.dev/dashboard](https://app.fedpulse.dev/dashboard)
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install @fedpulse/sdk
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { FedPulse } from '@fedpulse/sdk';
|
|
42
|
+
|
|
43
|
+
const client = new FedPulse({ apiKey: process.env.FEDPULSE_API_KEY! });
|
|
44
|
+
|
|
45
|
+
// Search opportunities
|
|
46
|
+
const result = await client.opportunities.list({
|
|
47
|
+
q: 'cybersecurity',
|
|
48
|
+
naics: ['541519', '541512'],
|
|
49
|
+
postedAfter: '2025-01-01',
|
|
50
|
+
limit: 25,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
console.log(result.data); // typed OpportunitySummary[]
|
|
54
|
+
console.log(result.pagination); // { total, page, limit, … }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Authentication
|
|
60
|
+
|
|
61
|
+
Pass your API key to the constructor. It is sent as `Authorization: Bearer <key>` on every request.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
const client = new FedPulse({ apiKey: 'fp_live_…' });
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Never hard-code keys in source — use environment variables.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Resources
|
|
72
|
+
|
|
73
|
+
### `client.opportunities`
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
// List / search opportunities
|
|
77
|
+
await client.opportunities.list({ q: 'cloud', naics: ['541512'] });
|
|
78
|
+
|
|
79
|
+
// Retrieve a single opportunity
|
|
80
|
+
await client.opportunities.get('abc123-notice-id');
|
|
81
|
+
|
|
82
|
+
// Aggregate statistics
|
|
83
|
+
await client.opportunities.stats({ postedAfter: '2025-01-01' });
|
|
84
|
+
|
|
85
|
+
// Request a data export (async job — returns immediately with a job ID; poll exportId for status)
|
|
86
|
+
await client.opportunities.createExport({ format: 'csv', q: 'AI' });
|
|
87
|
+
|
|
88
|
+
// Async generator — walks all pages automatically
|
|
89
|
+
for await (const page of client.opportunities.paginate({ q: 'defense' })) {
|
|
90
|
+
console.log(page.data);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### `client.exclusions`
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
// List all current exclusions
|
|
98
|
+
await client.exclusions.list({ activeOnly: true });
|
|
99
|
+
|
|
100
|
+
// Retrieve one record
|
|
101
|
+
await client.exclusions.get('exclusion-id');
|
|
102
|
+
|
|
103
|
+
// Bulk check up to 100 entities
|
|
104
|
+
await client.exclusions.check({
|
|
105
|
+
entities: [
|
|
106
|
+
{ uei: 'ABC123DEF456' },
|
|
107
|
+
{ cage: '1A2B3' },
|
|
108
|
+
{ name: 'Acme Corp' },
|
|
109
|
+
],
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Stats
|
|
113
|
+
await client.exclusions.stats();
|
|
114
|
+
|
|
115
|
+
// Page through all exclusions
|
|
116
|
+
for await (const page of client.exclusions.paginate()) {
|
|
117
|
+
console.log(page.data);
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `client.entities`
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
// List registered entities
|
|
125
|
+
await client.entities.list({ naics: ['336411'], state: 'VA' });
|
|
126
|
+
|
|
127
|
+
// Entity by UEI
|
|
128
|
+
await client.entities.get('ABCDEF123456');
|
|
129
|
+
|
|
130
|
+
// Contracts awarded to an entity
|
|
131
|
+
await client.entities.opportunities('ABCDEF123456', { limit: 50 });
|
|
132
|
+
|
|
133
|
+
// Exclusion check (never cached)
|
|
134
|
+
await client.entities.exclusionCheck('ABCDEF123456');
|
|
135
|
+
|
|
136
|
+
// Stats
|
|
137
|
+
await client.entities.stats();
|
|
138
|
+
|
|
139
|
+
// All pages
|
|
140
|
+
for await (const page of client.entities.paginate({ state: 'TX' })) {
|
|
141
|
+
console.log(page.data);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### `client.intelligence`
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
// 360° entity intelligence profile
|
|
149
|
+
await client.intelligence.entityProfile('ABCDEF123456');
|
|
150
|
+
|
|
151
|
+
// Market analysis for a NAICS code
|
|
152
|
+
await client.intelligence.marketAnalysis('541512');
|
|
153
|
+
|
|
154
|
+
// Single-entity compliance check
|
|
155
|
+
await client.intelligence.complianceCheck({ uei: 'ABCDEF123456' });
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### `client.assistance`
|
|
159
|
+
|
|
160
|
+
Federal grants and assistance listings (CFDA programs).
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
// Search listings
|
|
164
|
+
await client.assistance.list({ q: 'broadband', agency: 'USDA' });
|
|
165
|
+
|
|
166
|
+
// Retrieve one by CFDA program number (format: "XX.XXX")
|
|
167
|
+
await client.assistance.get('10.001');
|
|
168
|
+
|
|
169
|
+
// Aggregated stats
|
|
170
|
+
await client.assistance.stats();
|
|
171
|
+
|
|
172
|
+
// Auto-paginate all listings
|
|
173
|
+
for await (const page of client.assistance.paginate({ agency: 'HHS' })) {
|
|
174
|
+
console.log(page.data);
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### `client.analytics`
|
|
179
|
+
|
|
180
|
+
Per-user API usage analytics — scoped to the authenticated key.
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
// KPI totals (requests today, this month, error rates)
|
|
184
|
+
await client.analytics.summary({ range: '30d' });
|
|
185
|
+
|
|
186
|
+
// Per-day breakdown for charting (up to 90 days)
|
|
187
|
+
await client.analytics.chart({ range: '30d' });
|
|
188
|
+
|
|
189
|
+
// Top endpoints by request volume and P95 latency
|
|
190
|
+
await client.analytics.endpoints({ range: '7d' });
|
|
191
|
+
|
|
192
|
+
// Paginated raw request log
|
|
193
|
+
await client.analytics.logs({ limit: 20 });
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### `client.webhooks`
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
// List configured webhooks
|
|
200
|
+
await client.webhooks.list();
|
|
201
|
+
|
|
202
|
+
// Get a single webhook
|
|
203
|
+
await client.webhooks.get('00000000-0000-0000-0000-000000000001');
|
|
204
|
+
|
|
205
|
+
// Create
|
|
206
|
+
await client.webhooks.create({ url: 'https://example.com/hook', events: ['opportunity.new'] });
|
|
207
|
+
|
|
208
|
+
// Update / delete
|
|
209
|
+
await client.webhooks.update('00000000-0000-0000-0000-000000000001', { is_active: false });
|
|
210
|
+
await client.webhooks.delete('00000000-0000-0000-0000-000000000001');
|
|
211
|
+
|
|
212
|
+
// Send a test event to the endpoint immediately
|
|
213
|
+
await client.webhooks.test('00000000-0000-0000-0000-000000000001');
|
|
214
|
+
|
|
215
|
+
// Resume a paused webhook (auto-paused after 5 consecutive failures)
|
|
216
|
+
await client.webhooks.resume('00000000-0000-0000-0000-000000000001');
|
|
217
|
+
|
|
218
|
+
// Deliveries log
|
|
219
|
+
await client.webhooks.listDeliveries('00000000-0000-0000-0000-000000000001', { status: 'failed' });
|
|
220
|
+
await client.webhooks.getDelivery('00000000-0000-0000-0000-000000000001', '00000000-0000-0000-0000-000000000002');
|
|
221
|
+
|
|
222
|
+
// Auto-paginate all delivery history
|
|
223
|
+
for await (const page of client.webhooks.deliveryPages('00000000-0000-0000-0000-000000000001', { status: 'failed' })) {
|
|
224
|
+
console.log(page.data);
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Webhook signature verification
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
import { FedPulse } from '@fedpulse/sdk';
|
|
234
|
+
|
|
235
|
+
// In your Express / Fastify handler:
|
|
236
|
+
app.post('/webhooks', (req, res) => {
|
|
237
|
+
const { signatureHeader, timestampHeader } = FedPulse.extractWebhookHeaders(req.headers);
|
|
238
|
+
|
|
239
|
+
let event;
|
|
240
|
+
try {
|
|
241
|
+
event = FedPulse.verifyWebhook({
|
|
242
|
+
rawBody: req.body, // Buffer or string — the raw unparsed body
|
|
243
|
+
signatureHeader,
|
|
244
|
+
timestampHeader,
|
|
245
|
+
secret: process.env.FEDPULSE_WEBHOOK_SECRET!,
|
|
246
|
+
options: { maxAgeSeconds: 300 },
|
|
247
|
+
});
|
|
248
|
+
} catch (err) {
|
|
249
|
+
return res.status(400).send('Invalid signature');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
console.log(event.event, event.data);
|
|
253
|
+
res.sendStatus(200);
|
|
254
|
+
});
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Pagination helpers
|
|
260
|
+
|
|
261
|
+
Every list resource exposes a `paginate()` async generator that walks all pages for you:
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
const allOpps: OpportunitySummary[] = [];
|
|
265
|
+
|
|
266
|
+
for await (const page of client.opportunities.paginate({ q: 'cloud' })) {
|
|
267
|
+
allOpps.push(...page.data);
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Error handling
|
|
274
|
+
|
|
275
|
+
All errors extend `FedPulseError`. Import specific classes for precise handling:
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
import {
|
|
279
|
+
FedPulse,
|
|
280
|
+
AuthenticationError,
|
|
281
|
+
PermissionError,
|
|
282
|
+
NotFoundError,
|
|
283
|
+
ValidationError,
|
|
284
|
+
RateLimitError,
|
|
285
|
+
ServerError,
|
|
286
|
+
NetworkError,
|
|
287
|
+
TimeoutError,
|
|
288
|
+
RetryExhaustedError,
|
|
289
|
+
} from '@fedpulse/sdk';
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
await client.opportunities.get('bad-id');
|
|
293
|
+
} catch (err) {
|
|
294
|
+
if (err instanceof NotFoundError) {
|
|
295
|
+
console.log('Not found');
|
|
296
|
+
} else if (err instanceof RateLimitError) {
|
|
297
|
+
console.log(`Rate limited. Retry after: ${err.retryAfter}s`);
|
|
298
|
+
} else if (err instanceof RetryExhaustedError) {
|
|
299
|
+
console.log(`Failed after ${err.attempts} attempts`, err.lastError);
|
|
300
|
+
} else if (err instanceof NetworkError) {
|
|
301
|
+
console.log('Network issue — check internet connection');
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
| Class | Status | When thrown |
|
|
307
|
+
|---|---|---|
|
|
308
|
+
| `AuthenticationError` | 401 | Invalid or missing API key |
|
|
309
|
+
| `PermissionError` | 403 | Key lacks required permission |
|
|
310
|
+
| `NotFoundError` | 404 | Resource does not exist |
|
|
311
|
+
| `ValidationError` | 400/422 | Bad request parameters |
|
|
312
|
+
| `RateLimitError` | 429 | Too many requests (check `retryAfter`) |
|
|
313
|
+
| `ServerError` | 5xx | FedPulse server error |
|
|
314
|
+
| `NetworkError` | — | DNS failure, ECONNREFUSED, etc. |
|
|
315
|
+
| `TimeoutError` | — | Request exceeded `timeoutMs` |
|
|
316
|
+
| `RetryExhaustedError` | — | All retry attempts failed |
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Configuration
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
const client = new FedPulse({
|
|
324
|
+
apiKey: 'fp_live_…', // required
|
|
325
|
+
baseUrl: 'https://api.fedpulse.dev', // optional override
|
|
326
|
+
timeoutMs: 30_000, // per-attempt timeout (default: 30 s)
|
|
327
|
+
maxRetries: 3, // retries for 5xx / network errors (default: 3)
|
|
328
|
+
cacheSize: 256, // LRU cache entries, 0 to disable (default: 256)
|
|
329
|
+
cacheTtlMs: 60_000, // cache TTL in ms (default: 60 s)
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Rate limit info
|
|
334
|
+
|
|
335
|
+
```ts
|
|
336
|
+
const { data } = await client.opportunities.list({ q: 'cloud' });
|
|
337
|
+
console.log(client.rateLimit);
|
|
338
|
+
// { limit: 1000, remaining: 987, reset: 1740000000 }
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Clear cache
|
|
342
|
+
|
|
343
|
+
```ts
|
|
344
|
+
client.clearCache();
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## CommonJS usage
|
|
350
|
+
|
|
351
|
+
```js
|
|
352
|
+
const { FedPulse } = require('@fedpulse/sdk');
|
|
353
|
+
const client = new FedPulse({ apiKey: process.env.FEDPULSE_API_KEY });
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## License
|
|
359
|
+
|
|
360
|
+
MIT © [FedPulse](https://fedpulse.dev)
|
package/package.json
CHANGED