@fedpulse/sdk 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.
Files changed (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +331 -0
  3. package/dist/cjs/client.cjs +138 -0
  4. package/dist/cjs/errors.cjs +200 -0
  5. package/dist/cjs/http.cjs +449 -0
  6. package/dist/cjs/index.cjs +65 -0
  7. package/dist/cjs/resources/analytics.cjs +134 -0
  8. package/dist/cjs/resources/assistance.cjs +101 -0
  9. package/dist/cjs/resources/entities.cjs +149 -0
  10. package/dist/cjs/resources/exclusions.cjs +135 -0
  11. package/dist/cjs/resources/intelligence.cjs +96 -0
  12. package/dist/cjs/resources/opportunities.cjs +170 -0
  13. package/dist/cjs/resources/webhooks.cjs +262 -0
  14. package/dist/cjs/types/analytics.cjs +5 -0
  15. package/dist/cjs/types/assistance.cjs +5 -0
  16. package/dist/cjs/types/common.cjs +5 -0
  17. package/dist/cjs/types/entities.cjs +5 -0
  18. package/dist/cjs/types/exclusions.cjs +5 -0
  19. package/dist/cjs/types/index.cjs +5 -0
  20. package/dist/cjs/types/intelligence.cjs +5 -0
  21. package/dist/cjs/types/opportunities.cjs +5 -0
  22. package/dist/cjs/types/webhooks.cjs +5 -0
  23. package/dist/cjs/webhooks-verify.cjs +184 -0
  24. package/dist/esm/client.js +135 -0
  25. package/dist/esm/client.js.map +1 -0
  26. package/dist/esm/errors.js +187 -0
  27. package/dist/esm/errors.js.map +1 -0
  28. package/dist/esm/http.js +445 -0
  29. package/dist/esm/http.js.map +1 -0
  30. package/dist/esm/index.js +40 -0
  31. package/dist/esm/index.js.map +1 -0
  32. package/dist/esm/resources/analytics.js +131 -0
  33. package/dist/esm/resources/analytics.js.map +1 -0
  34. package/dist/esm/resources/assistance.js +98 -0
  35. package/dist/esm/resources/assistance.js.map +1 -0
  36. package/dist/esm/resources/entities.js +146 -0
  37. package/dist/esm/resources/entities.js.map +1 -0
  38. package/dist/esm/resources/exclusions.js +132 -0
  39. package/dist/esm/resources/exclusions.js.map +1 -0
  40. package/dist/esm/resources/intelligence.js +93 -0
  41. package/dist/esm/resources/intelligence.js.map +1 -0
  42. package/dist/esm/resources/opportunities.js +167 -0
  43. package/dist/esm/resources/opportunities.js.map +1 -0
  44. package/dist/esm/resources/webhooks.js +259 -0
  45. package/dist/esm/resources/webhooks.js.map +1 -0
  46. package/dist/esm/types/analytics.js +5 -0
  47. package/dist/esm/types/analytics.js.map +1 -0
  48. package/dist/esm/types/assistance.js +5 -0
  49. package/dist/esm/types/assistance.js.map +1 -0
  50. package/dist/esm/types/common.js +5 -0
  51. package/dist/esm/types/common.js.map +1 -0
  52. package/dist/esm/types/entities.js +5 -0
  53. package/dist/esm/types/entities.js.map +1 -0
  54. package/dist/esm/types/exclusions.js +5 -0
  55. package/dist/esm/types/exclusions.js.map +1 -0
  56. package/dist/esm/types/index.js +5 -0
  57. package/dist/esm/types/index.js.map +1 -0
  58. package/dist/esm/types/intelligence.js +5 -0
  59. package/dist/esm/types/intelligence.js.map +1 -0
  60. package/dist/esm/types/opportunities.js +5 -0
  61. package/dist/esm/types/opportunities.js.map +1 -0
  62. package/dist/esm/types/webhooks.js +5 -0
  63. package/dist/esm/types/webhooks.js.map +1 -0
  64. package/dist/esm/webhooks-verify.js +179 -0
  65. package/dist/esm/webhooks-verify.js.map +1 -0
  66. package/dist/types/client.d.cts +136 -0
  67. package/dist/types/client.d.ts +136 -0
  68. package/dist/types/client.d.ts.map +1 -0
  69. package/dist/types/errors.d.cts +139 -0
  70. package/dist/types/errors.d.ts +139 -0
  71. package/dist/types/errors.d.ts.map +1 -0
  72. package/dist/types/http.d.cts +137 -0
  73. package/dist/types/http.d.ts +137 -0
  74. package/dist/types/http.d.ts.map +1 -0
  75. package/dist/types/index.d.cts +39 -0
  76. package/dist/types/index.d.ts +39 -0
  77. package/dist/types/index.d.ts.map +1 -0
  78. package/dist/types/resources/analytics.d.cts +94 -0
  79. package/dist/types/resources/analytics.d.ts +94 -0
  80. package/dist/types/resources/analytics.d.ts.map +1 -0
  81. package/dist/types/resources/assistance.d.cts +66 -0
  82. package/dist/types/resources/assistance.d.ts +66 -0
  83. package/dist/types/resources/assistance.d.ts.map +1 -0
  84. package/dist/types/resources/entities.d.cts +101 -0
  85. package/dist/types/resources/entities.d.ts +101 -0
  86. package/dist/types/resources/entities.d.ts.map +1 -0
  87. package/dist/types/resources/exclusions.d.cts +84 -0
  88. package/dist/types/resources/exclusions.d.ts +84 -0
  89. package/dist/types/resources/exclusions.d.ts.map +1 -0
  90. package/dist/types/resources/intelligence.d.cts +66 -0
  91. package/dist/types/resources/intelligence.d.ts +66 -0
  92. package/dist/types/resources/intelligence.d.ts.map +1 -0
  93. package/dist/types/resources/opportunities.d.cts +116 -0
  94. package/dist/types/resources/opportunities.d.ts +116 -0
  95. package/dist/types/resources/opportunities.d.ts.map +1 -0
  96. package/dist/types/resources/webhooks.d.cts +180 -0
  97. package/dist/types/resources/webhooks.d.ts +180 -0
  98. package/dist/types/resources/webhooks.d.ts.map +1 -0
  99. package/dist/types/types/analytics.d.cts +85 -0
  100. package/dist/types/types/analytics.d.ts +85 -0
  101. package/dist/types/types/analytics.d.ts.map +1 -0
  102. package/dist/types/types/assistance.d.cts +55 -0
  103. package/dist/types/types/assistance.d.ts +55 -0
  104. package/dist/types/types/assistance.d.ts.map +1 -0
  105. package/dist/types/types/common.d.cts +58 -0
  106. package/dist/types/types/common.d.ts +58 -0
  107. package/dist/types/types/common.d.ts.map +1 -0
  108. package/dist/types/types/entities.d.cts +85 -0
  109. package/dist/types/types/entities.d.ts +85 -0
  110. package/dist/types/types/entities.d.ts.map +1 -0
  111. package/dist/types/types/exclusions.d.cts +81 -0
  112. package/dist/types/types/exclusions.d.ts +81 -0
  113. package/dist/types/types/exclusions.d.ts.map +1 -0
  114. package/dist/types/types/index.d.cts +12 -0
  115. package/dist/types/types/index.d.ts +12 -0
  116. package/dist/types/types/index.d.ts.map +1 -0
  117. package/dist/types/types/intelligence.d.cts +104 -0
  118. package/dist/types/types/intelligence.d.ts +104 -0
  119. package/dist/types/types/intelligence.d.ts.map +1 -0
  120. package/dist/types/types/opportunities.d.cts +149 -0
  121. package/dist/types/types/opportunities.d.ts +149 -0
  122. package/dist/types/types/opportunities.d.ts.map +1 -0
  123. package/dist/types/types/webhooks.d.cts +106 -0
  124. package/dist/types/types/webhooks.d.ts +106 -0
  125. package/dist/types/types/webhooks.d.ts.map +1 -0
  126. package/dist/types/webhooks-verify.d.cts +102 -0
  127. package/dist/types/webhooks-verify.d.ts +102 -0
  128. package/dist/types/webhooks-verify.d.ts.map +1 -0
  129. package/package.json +62 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FedPulse
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,331 @@
1
+ # @fedpulse/sdk
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@fedpulse/sdk)](https://www.npmjs.com/package/@fedpulse/sdk)
4
+ [![Node.js ≥18](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
+ keyword: '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({ keyword: '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', filter: { keyword: 'AI' } });
87
+
88
+ // Async generator — walks all pages automatically
89
+ for await (const page of client.opportunities.paginate({ keyword: '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({ active: 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
+ // Bulk compliance check
155
+ await client.intelligence.complianceCheck({
156
+ entities: [{ uei: 'ABCDEF123456' }, { name: 'Bad Actor LLC' }],
157
+ });
158
+ ```
159
+
160
+ ### `client.assistance`
161
+
162
+ Federal grants and assistance listings.
163
+
164
+ ```ts
165
+ await client.assistance.list({ keyword: 'broadband', agency: 'USDA' });
166
+ await client.assistance.get('listing-id');
167
+ await client.assistance.getByProgram('10.001');
168
+ await client.assistance.stats();
169
+ for await (const page of client.assistance.paginate()) { … }
170
+ ```
171
+
172
+ ### `client.analytics`
173
+
174
+ ```ts
175
+ await client.analytics.overview({ period: '30d' });
176
+ await client.analytics.usageSummary();
177
+ await client.analytics.topKeywords({ limit: 20 });
178
+ ```
179
+
180
+ ### `client.webhooks`
181
+
182
+ ```ts
183
+ // List configured webhooks
184
+ await client.webhooks.list();
185
+
186
+ // Create
187
+ await client.webhooks.create({ url: 'https://example.com/hook', events: ['opportunity.posted'] });
188
+
189
+ // Update / delete
190
+ await client.webhooks.update('wh_id', { active: false });
191
+ await client.webhooks.delete('wh_id');
192
+
193
+ // Deliveries log
194
+ await client.webhooks.listDeliveries('wh_id');
195
+ await client.webhooks.getDelivery('wh_id', 'delivery-id');
196
+ await client.webhooks.redeliver('wh_id', 'delivery-id');
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Webhook signature verification
202
+
203
+ ```ts
204
+ import { FedPulse } from '@fedpulse/sdk';
205
+
206
+ // In your Express / Fastify handler:
207
+ app.post('/webhooks', (req, res) => {
208
+ const { signatureHeader, timestampHeader } = FedPulse.extractWebhookHeaders(req.headers);
209
+
210
+ let event;
211
+ try {
212
+ event = FedPulse.verifyWebhook({
213
+ rawBody: req.body, // Buffer or string — the raw unparsed body
214
+ signatureHeader,
215
+ timestampHeader,
216
+ secret: process.env.FEDPULSE_WEBHOOK_SECRET!,
217
+ options: { maxAgeSeconds: 300 },
218
+ });
219
+ } catch (err) {
220
+ return res.status(400).send('Invalid signature');
221
+ }
222
+
223
+ console.log(event.event, event.data);
224
+ res.sendStatus(200);
225
+ });
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Pagination helpers
231
+
232
+ Every list resource exposes a `paginate()` async generator that walks all pages for you:
233
+
234
+ ```ts
235
+ const allOpps: OpportunitySummary[] = [];
236
+
237
+ for await (const page of client.opportunities.paginate({ keyword: 'cloud' })) {
238
+ allOpps.push(...page.data);
239
+ }
240
+ ```
241
+
242
+ ---
243
+
244
+ ## Error handling
245
+
246
+ All errors extend `FedPulseError`. Import specific classes for precise handling:
247
+
248
+ ```ts
249
+ import {
250
+ FedPulse,
251
+ AuthenticationError,
252
+ PermissionError,
253
+ NotFoundError,
254
+ ValidationError,
255
+ RateLimitError,
256
+ ServerError,
257
+ NetworkError,
258
+ TimeoutError,
259
+ RetryExhaustedError,
260
+ } from '@fedpulse/sdk';
261
+
262
+ try {
263
+ await client.opportunities.get('bad-id');
264
+ } catch (err) {
265
+ if (err instanceof NotFoundError) {
266
+ console.log('Not found');
267
+ } else if (err instanceof RateLimitError) {
268
+ console.log(`Rate limited. Retry after: ${err.retryAfter}s`);
269
+ } else if (err instanceof RetryExhaustedError) {
270
+ console.log(`Failed after ${err.attempts} attempts`, err.lastError);
271
+ } else if (err instanceof NetworkError) {
272
+ console.log('Network issue — check internet connection');
273
+ }
274
+ }
275
+ ```
276
+
277
+ | Class | Status | When thrown |
278
+ |---|---|---|
279
+ | `AuthenticationError` | 401 | Invalid or missing API key |
280
+ | `PermissionError` | 403 | Key lacks required permission |
281
+ | `NotFoundError` | 404 | Resource does not exist |
282
+ | `ValidationError` | 400/422 | Bad request parameters |
283
+ | `RateLimitError` | 429 | Too many requests (check `retryAfter`) |
284
+ | `ServerError` | 5xx | FedPulse server error |
285
+ | `NetworkError` | — | DNS failure, ECONNREFUSED, etc. |
286
+ | `TimeoutError` | — | Request exceeded `timeoutMs` |
287
+ | `RetryExhaustedError` | — | All retry attempts failed |
288
+
289
+ ---
290
+
291
+ ## Configuration
292
+
293
+ ```ts
294
+ const client = new FedPulse({
295
+ apiKey: 'fp_live_…', // required
296
+ baseUrl: 'https://api.fedpulse.dev', // optional override
297
+ timeoutMs: 30_000, // per-attempt timeout (default: 30 s)
298
+ maxRetries: 3, // retries for 5xx / network errors (default: 3)
299
+ cacheSize: 256, // LRU cache entries, 0 to disable (default: 256)
300
+ cacheTtlMs: 60_000, // cache TTL in ms (default: 60 s)
301
+ });
302
+ ```
303
+
304
+ ### Rate limit info
305
+
306
+ ```ts
307
+ const { data } = await client.opportunities.list({ keyword: 'cloud' });
308
+ console.log(client.rateLimit);
309
+ // { limit: 1000, remaining: 987, reset: 1740000000 }
310
+ ```
311
+
312
+ ### Clear cache
313
+
314
+ ```ts
315
+ client.clearCache();
316
+ ```
317
+
318
+ ---
319
+
320
+ ## CommonJS usage
321
+
322
+ ```js
323
+ const { FedPulse } = require('@fedpulse/sdk');
324
+ const client = new FedPulse({ apiKey: process.env.FEDPULSE_API_KEY });
325
+ ```
326
+
327
+ ---
328
+
329
+ ## License
330
+
331
+ MIT © [FedPulse](https://fedpulse.dev)
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ /**
3
+ * FedPulse SDK — main client class.
4
+ *
5
+ * Instantiate this class with your API key to access all FedPulse
6
+ * data resources and the webhook verification utility.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { FedPulse } from '@fedpulse/sdk';
11
+ *
12
+ * const client = new FedPulse({ apiKey: process.env.FEDPULSE_API_KEY! });
13
+ *
14
+ * // Search contracts
15
+ * const { data } = await client.opportunities.list({ q: 'cloud', naics: '541512' });
16
+ *
17
+ * // Compliance check
18
+ * const { data: status } = await client.exclusions.check({ entities: [{ uei: 'ABCDEF123456' }] });
19
+ *
20
+ * // Verify an incoming webhook
21
+ * const payload = FedPulse.verifyWebhook({ rawBody, signatureHeader, timestampHeader, secret });
22
+ * ```
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.WebhookVerificationError = exports.FedPulse = void 0;
26
+ const http_js_1 = require("./http.cjs");
27
+ const opportunities_js_1 = require("./resources/opportunities.cjs");
28
+ const exclusions_js_1 = require("./resources/exclusions.cjs");
29
+ const entities_js_1 = require("./resources/entities.cjs");
30
+ const intelligence_js_1 = require("./resources/intelligence.cjs");
31
+ const assistance_js_1 = require("./resources/assistance.cjs");
32
+ const analytics_js_1 = require("./resources/analytics.cjs");
33
+ const webhooks_js_1 = require("./resources/webhooks.cjs");
34
+ const webhooks_verify_js_1 = require("./webhooks-verify.cjs");
35
+ Object.defineProperty(exports, "WebhookVerificationError", { enumerable: true, get: function () { return webhooks_verify_js_1.WebhookVerificationError; } });
36
+ // ── Main client class ──────────────────────────────────────────────────────────
37
+ /**
38
+ * The FedPulse SDK client.
39
+ *
40
+ * All API interactions go through the resource properties on this class.
41
+ * The static `verifyWebhook` method can be used independently of a client instance.
42
+ */
43
+ class FedPulse {
44
+ /** Low-level HTTP client (exposed for advanced usage only). */
45
+ http;
46
+ /** Federal contract opportunities (/v1/opportunities). */
47
+ opportunities;
48
+ /** SAM.gov exclusions and bulk compliance checks (/v1/exclusions). */
49
+ exclusions;
50
+ /** SAM.gov registered entities / vendors (/v1/entities). */
51
+ entities;
52
+ /** 360° entity intelligence and market analysis (/v1/intelligence). */
53
+ intelligence;
54
+ /** Federal assistance listings / CFDA programs (/v1/assistance). */
55
+ assistance;
56
+ /** Per-user API usage analytics (/v1/analytics). */
57
+ analytics;
58
+ /** Webhook subscription management (/v1/webhooks). */
59
+ webhooks;
60
+ constructor(options) {
61
+ this.http = new http_js_1.HttpClient(options);
62
+ this.opportunities = new opportunities_js_1.OpportunitiesResource(this.http);
63
+ this.exclusions = new exclusions_js_1.ExclusionsResource(this.http);
64
+ this.entities = new entities_js_1.EntitiesResource(this.http);
65
+ this.intelligence = new intelligence_js_1.IntelligenceResource(this.http);
66
+ this.assistance = new assistance_js_1.AssistanceResource(this.http);
67
+ this.analytics = new analytics_js_1.AnalyticsResource(this.http);
68
+ this.webhooks = new webhooks_js_1.WebhooksResource(this.http);
69
+ }
70
+ /**
71
+ * Most recent rate-limit info observed from API responses.
72
+ *
73
+ * Updated after every API call. Useful for monitoring your rate-limit usage.
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * await client.opportunities.list({ limit: 25 });
78
+ * console.log('Remaining requests:', client.rateLimit?.remaining);
79
+ * ```
80
+ */
81
+ get rateLimit() {
82
+ return this.http.lastRateLimit;
83
+ }
84
+ /**
85
+ * Clear the in-memory response cache.
86
+ *
87
+ * Useful after writes that may invalidate cached GET responses.
88
+ */
89
+ clearCache() {
90
+ this.http.clearCache();
91
+ }
92
+ // ── Static webhook utilities ───────────────────────────────────────────────
93
+ /**
94
+ * Verify an incoming FedPulse webhook delivery.
95
+ *
96
+ * Validates the HMAC-SHA256 signature, checks the timestamp against replay
97
+ * attacks, and returns the parsed payload on success.
98
+ *
99
+ * **IMPORTANT:** Pass the raw request body bytes — do not parse to JSON first.
100
+ *
101
+ * @param input Headers, raw body, and signing secret.
102
+ * @returns Parsed, verified webhook payload.
103
+ * @throws {WebhookVerificationError} If signature/timestamp is invalid.
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * // Express.js with `express.raw({ type: 'application/json' })`:
108
+ * const payload = FedPulse.verifyWebhook<{ noticeId: string }>({
109
+ * rawBody: req.body, // Buffer from express.raw()
110
+ * signatureHeader: req.headers['x-fedpulse-signature'] as string,
111
+ * timestampHeader: req.headers['x-fedpulse-timestamp'] as string,
112
+ * secret: process.env.FEDPULSE_WEBHOOK_SECRET!,
113
+ * });
114
+ * console.log(payload.event, payload.data.noticeId);
115
+ * ```
116
+ */
117
+ static verifyWebhook(input) {
118
+ return (0, webhooks_verify_js_1.verifyWebhook)(input);
119
+ }
120
+ /**
121
+ * Extract FedPulse webhook headers from a request headers object.
122
+ *
123
+ * Handles case-insensitive lookup across Express, Fastify, Next.js, etc.
124
+ *
125
+ * @param headers Headers object (plain object or `Headers` instance).
126
+ * @returns Signature header, timestamp header, event type, and delivery ID.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * const { signatureHeader, timestampHeader } = FedPulse.extractWebhookHeaders(req.headers);
131
+ * const payload = FedPulse.verifyWebhook({ rawBody, signatureHeader, timestampHeader, secret });
132
+ * ```
133
+ */
134
+ static extractWebhookHeaders(headers) {
135
+ return (0, webhooks_verify_js_1.extractWebhookHeaders)(headers);
136
+ }
137
+ }
138
+ exports.FedPulse = FedPulse;
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ /**
3
+ * FedPulse SDK error classes.
4
+ *
5
+ * All errors thrown by the SDK extend FedPulseError so callers can use
6
+ * `instanceof FedPulseError` to distinguish SDK errors from other exceptions.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.RetryExhaustedError = exports.TimeoutError = exports.NetworkError = exports.ServerError = exports.RateLimitError = exports.ValidationError = exports.NotFoundError = exports.PermissionError = exports.AuthenticationError = exports.FedPulseError = void 0;
10
+ exports.createApiError = createApiError;
11
+ // ── Base error ─────────────────────────────────────────────────────────────────
12
+ /**
13
+ * Base class for all FedPulse SDK errors.
14
+ */
15
+ class FedPulseError extends Error {
16
+ /** HTTP status code returned by the API, or 0 for network errors. */
17
+ status;
18
+ /** Machine-readable error code from the API (e.g. NOT_FOUND, RATE_LIMIT_EXCEEDED). */
19
+ code;
20
+ /** Request ID for debugging — correlates with API logs. */
21
+ requestId;
22
+ /** Additional structured details from the API response. */
23
+ details;
24
+ constructor({ message, status, code, requestId, details, }) {
25
+ super(message);
26
+ this.name = 'FedPulseError';
27
+ this.status = status;
28
+ this.code = code;
29
+ this.requestId = requestId;
30
+ this.details = details;
31
+ // Restore prototype chain after `extends Error` in transpiled output.
32
+ Object.setPrototypeOf(this, new.target.prototype);
33
+ }
34
+ }
35
+ exports.FedPulseError = FedPulseError;
36
+ // ── Specific error subtypes ────────────────────────────────────────────────────
37
+ /**
38
+ * Thrown when the API returns 401 Unauthorized.
39
+ * Usually caused by missing, invalid, or revoked API key / JWT.
40
+ */
41
+ class AuthenticationError extends FedPulseError {
42
+ constructor(params) {
43
+ super({ ...params, status: 401, code: 'UNAUTHORIZED' });
44
+ this.name = 'AuthenticationError';
45
+ Object.setPrototypeOf(this, new.target.prototype);
46
+ }
47
+ }
48
+ exports.AuthenticationError = AuthenticationError;
49
+ /**
50
+ * Thrown when the API returns 403 Forbidden.
51
+ * Usually caused by insufficient plan permissions or missing key scope.
52
+ */
53
+ class PermissionError extends FedPulseError {
54
+ constructor(params) {
55
+ super({ ...params, status: 403, code: params.code ?? 'FORBIDDEN' });
56
+ this.name = 'PermissionError';
57
+ Object.setPrototypeOf(this, new.target.prototype);
58
+ }
59
+ }
60
+ exports.PermissionError = PermissionError;
61
+ /**
62
+ * Thrown when the API returns 404 Not Found.
63
+ */
64
+ class NotFoundError extends FedPulseError {
65
+ constructor(params) {
66
+ super({ ...params, status: 404, code: 'NOT_FOUND' });
67
+ this.name = 'NotFoundError';
68
+ Object.setPrototypeOf(this, new.target.prototype);
69
+ }
70
+ }
71
+ exports.NotFoundError = NotFoundError;
72
+ /**
73
+ * Thrown when the API returns 422 Unprocessable Entity or 400 Bad Request
74
+ * due to invalid request parameters or body.
75
+ */
76
+ class ValidationError extends FedPulseError {
77
+ constructor(params) {
78
+ super({ ...params, status: params.status ?? 422, code: 'VALIDATION_ERROR' });
79
+ this.name = 'ValidationError';
80
+ Object.setPrototypeOf(this, new.target.prototype);
81
+ }
82
+ }
83
+ exports.ValidationError = ValidationError;
84
+ /**
85
+ * Thrown when the API returns 429 Too Many Requests.
86
+ * The `retryAfter` property indicates when the rate limit resets (Unix seconds).
87
+ */
88
+ class RateLimitError extends FedPulseError {
89
+ /** Unix epoch seconds when the rate limit window resets. */
90
+ retryAfter;
91
+ constructor(params) {
92
+ super({ ...params, status: 429, code: 'RATE_LIMIT_EXCEEDED' });
93
+ this.name = 'RateLimitError';
94
+ this.retryAfter = params.retryAfter;
95
+ Object.setPrototypeOf(this, new.target.prototype);
96
+ }
97
+ }
98
+ exports.RateLimitError = RateLimitError;
99
+ /**
100
+ * Thrown when the API returns a 5xx server error.
101
+ */
102
+ class ServerError extends FedPulseError {
103
+ constructor(params) {
104
+ super({ ...params, status: params.status ?? 500, code: params.code ?? 'INTERNAL_ERROR' });
105
+ this.name = 'ServerError';
106
+ Object.setPrototypeOf(this, new.target.prototype);
107
+ }
108
+ }
109
+ exports.ServerError = ServerError;
110
+ /**
111
+ * Thrown when a network-level failure occurs (DNS, TCP, timeout, ECONNREFUSED).
112
+ * The `cause` property contains the original error.
113
+ */
114
+ class NetworkError extends FedPulseError {
115
+ /** The underlying network error. */
116
+ cause;
117
+ constructor(params) {
118
+ super({ ...params, status: 0, code: 'NETWORK_ERROR' });
119
+ this.name = 'NetworkError';
120
+ this.cause = params.cause;
121
+ Object.setPrototypeOf(this, new.target.prototype);
122
+ }
123
+ }
124
+ exports.NetworkError = NetworkError;
125
+ /**
126
+ * Thrown when a request exceeds the configured timeout.
127
+ */
128
+ class TimeoutError extends FedPulseError {
129
+ constructor(params) {
130
+ super({ ...params, status: 0, code: 'REQUEST_TIMEOUT' });
131
+ this.name = 'TimeoutError';
132
+ Object.setPrototypeOf(this, new.target.prototype);
133
+ }
134
+ }
135
+ exports.TimeoutError = TimeoutError;
136
+ /**
137
+ * Thrown when the SDK's retry budget is exhausted.
138
+ * The `lastError` property contains the final underlying error.
139
+ */
140
+ class RetryExhaustedError extends FedPulseError {
141
+ lastError;
142
+ attempts;
143
+ constructor(params) {
144
+ super({
145
+ message: `Request failed after ${params.attempts} attempt(s): ${params.lastError.message}`,
146
+ status: params.lastError.status,
147
+ code: 'RETRY_EXHAUSTED',
148
+ requestId: params.lastError.requestId,
149
+ });
150
+ this.name = 'RetryExhaustedError';
151
+ this.lastError = params.lastError;
152
+ this.attempts = params.attempts;
153
+ Object.setPrototypeOf(this, new.target.prototype);
154
+ }
155
+ }
156
+ exports.RetryExhaustedError = RetryExhaustedError;
157
+ /**
158
+ * Factory — parses a raw API error response object into the correct error subtype.
159
+ *
160
+ * @param status HTTP response status code.
161
+ * @param body Parsed JSON body (may be the error envelope or anything).
162
+ * @param headers Response headers map (used for Retry-After).
163
+ */
164
+ function createApiError(status, body, headers) {
165
+ // Extract standardised error fields from the envelope.
166
+ const envelope = body != null && typeof body === 'object' ? body : {};
167
+ const errObj = envelope['error'] ?? {};
168
+ const message = typeof errObj['message'] === 'string'
169
+ ? errObj['message']
170
+ : typeof envelope['message'] === 'string'
171
+ ? envelope['message']
172
+ : `HTTP ${status}`;
173
+ const code = typeof errObj['code'] === 'string' ? errObj['code'] : undefined;
174
+ const requestId = typeof envelope['meta']?.['requestId'] === 'string'
175
+ ? envelope['meta']['requestId']
176
+ : undefined;
177
+ const details = errObj['details'] ?? undefined;
178
+ switch (status) {
179
+ case 400:
180
+ return new ValidationError({ message, status: 400, requestId, details });
181
+ case 401:
182
+ return new AuthenticationError({ message, requestId, details });
183
+ case 403:
184
+ return new PermissionError({ message, code, requestId, details });
185
+ case 404:
186
+ return new NotFoundError({ message, requestId, details });
187
+ case 422:
188
+ return new ValidationError({ message, status: 422, requestId, details });
189
+ case 429: {
190
+ const retryAfterHeader = headers['retry-after'] ?? headers['x-ratelimit-reset'];
191
+ const retryAfter = retryAfterHeader != null ? Number(retryAfterHeader) : undefined;
192
+ return new RateLimitError({ message, retryAfter: Number.isFinite(retryAfter) ? retryAfter : undefined, requestId, details });
193
+ }
194
+ default:
195
+ if (status >= 500) {
196
+ return new ServerError({ message, status, code, requestId, details });
197
+ }
198
+ return new FedPulseError({ message, status, code: code ?? 'UNKNOWN_ERROR', requestId, details });
199
+ }
200
+ }