Skip to content

Commit cee2364

Browse files
authored
feat: add new subscriptionStatus module (#425)
1 parent d8c0d98 commit cee2364

File tree

12 files changed

+196
-5
lines changed

12 files changed

+196
-5
lines changed

.bundlewatch.config.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{
44
"//": "Pre-bundled for Browser (UMD)",
55
"path": "dist/browser/hibp.umd.js",
6-
"maxSize": "9 kB"
6+
"maxSize": "9.2 kB"
77
},
88
{
99
"//": "Pre-bundled for Browser (ESM)",
@@ -43,6 +43,10 @@
4343
"path": "dist/cjs/search.js",
4444
"maxSize": "1.5 kB"
4545
},
46+
{
47+
"path": "dist/cjs/subscription-status.js",
48+
"maxSize": "1 kB"
49+
},
4650
{
4751
"//": "Bundled with Webpack (ESM)",
4852
"path": "dist/esm/breach.mjs",
@@ -75,6 +79,10 @@
7579
{
7680
"path": "dist/esm/search.mjs",
7781
"maxSize": "1.4 kB"
82+
},
83+
{
84+
"path": "dist/esm/subscription-status.mjs",
85+
"maxSize": "1 kB"
7886
}
7987
]
8088
}

.changeset/proud-carrots-cough.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'hibp': minor
3+
---
4+
5+
Add a new `subscriptionStatus` module for retrieving the current subscription status of your HIBP API key. See https://haveibeenpwned.com/API/v3#SubscriptionStatus for more information.

API.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ convenience method is designed to mimic.</p>
5757
required, but direct requests made without it (that is, without specifying a
5858
<code>baseUrl</code> to a proxy that inserts a valid API key on your behalf) will fail.</p>
5959
</dd>
60+
<dt><a href="#subscriptionStatus">subscriptionStatus(apiKey, [options])</a> ⇒ <code><a href="#subscriptionstatus--object">Promise.&lt;SubscriptionStatus&gt;</a></code></dt>
61+
<dd><p>Fetches the current status of your HIBP subscription (API key).</p>
62+
</dd>
6063
</dl>
6164

6265
## Typedefs
@@ -75,6 +78,9 @@ hash prefix) to how many times it occurred in the Pwned Passwords repository.</p
7578
<dt><a href="#SearchResults">SearchResults</a> : <code>object</code></dt>
7679
<dd><p>An object representing search results.</p>
7780
</dd>
81+
<dt><a href="#subscriptionstatus--object">SubscriptionStatus</a> : <code>object</code></dt>
82+
<dd><p>An object representing the status of your HIBP subscription.</p>
83+
</dd>
7884
</dl>
7985

8086
<a name="breach"></a>
@@ -444,6 +450,31 @@ try {
444450
// ...
445451
}
446452
```
453+
<a name="subscriptionStatus"></a>
454+
455+
## subscriptionStatus(apiKey, [options]) ⇒ [<code>Promise.&lt;SubscriptionStatus&gt;</code>](#subscriptionstatus--object)
456+
Fetches the current status of your HIBP subscription (API key).
457+
458+
**Kind**: global function
459+
**Returns**: [<code>Promise.&lt;SubscriptionStatus&gt;</code>](#subscriptionstatus--object) - a Promise which resolves to a
460+
subscription status object, or rejects with an Error
461+
462+
| Param | Type | Description |
463+
| --- | --- | --- |
464+
| apiKey | <code>string</code> | an API key from https://haveibeenpwned.com/API/Key |
465+
| [options] | <code>object</code> | a configuration object |
466+
| [options.baseUrl] | <code>string</code> | a custom base URL for the haveibeenpwned.com API endpoints (default: `https://haveibeenpwned.com/api/v3`) |
467+
| [options.userAgent] | <code>string</code> | a custom string to send as the User-Agent field in the request headers (default: `hibp <version>`) |
468+
469+
**Example**
470+
```js
471+
try {
472+
const data = await subscriptionStatus("my-api-key");
473+
// ...
474+
} catch (err) {
475+
// ...
476+
}
477+
```
447478
<a name="Breach"></a>
448479

449480
## Breach : <code>object</code>
@@ -508,3 +539,19 @@ An object representing search results.
508539
| breaches | [<code>Array.&lt;Breach&gt;</code>](#breach--object) \| <code>null</code> |
509540
| pastes | [<code>Array.&lt;Paste&gt;</code>](#Paste) \| <code>null</code> |
510541

542+
<a name="SubscriptionStatus"></a>
543+
544+
## SubscriptionStatus : <code>object</code>
545+
An object representing the status of your HIBP subscription.
546+
547+
**Kind**: global typedef
548+
**Properties**
549+
550+
| Name | Type |
551+
| --- | --- |
552+
| SubscriptionName | <code>string</code> |
553+
| Description | <code>string</code> |
554+
| SubscribedUntil | <code>string</code> |
555+
| Rpm | <code>number</code> |
556+
| DomainSearchMaxBreachedAccounts | <code>number</code> |
557+

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use it in the browser.
4646
a data breach
4747
- Check a SHA-1 or NTLM prefix to see if it has been exposed in a data breach
4848
- Search for an account in both breaches and pastes at the same time 🔑
49+
- Get your subscription status 🔑
4950
- All queries return a Promise
5051
- Available server-side (Node.js) and client-side (browser)
5152
- Written in TypeScript, so all modules come fully typed
@@ -80,6 +81,7 @@ The following modules are available:
8081
- [pwnedPassword](API.md#pwnedpassword)
8182
- [pwnedPasswordRange](API.md#pwnedpasswordrange)
8283
- [search](API.md#search)
84+
- [subscriptionStatus](API.md#subscriptionstatus)
8385

8486
Please see the [API reference](API.md) for more detailed usage information and
8587
examples.

docs/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use it in the browser.
4141
check a password to see if it has been exposed in a data breach
4242
- Check a SHA-1 or NTLM prefix to see if it has been exposed in a data breach
4343
- Search for an account in both breaches and pastes at the same time 🔑
44+
- Get your subscription status 🔑
4445
- All queries return a Promise
4546
- Available server-side (Node.js) and client-side (browser)
4647
- Written in TypeScript, so all modules come fully typed
@@ -75,6 +76,7 @@ The following modules are available:
7576
- [pwnedPassword](https://github.com/wKovacs64/hibp/tree/main/API.md#pwnedpassword)
7677
- [pwnedPasswordRange](https://github.com/wKovacs64/hibp/tree/main/API.md#pwnedpasswordrange)
7778
- [search](https://github.com/wKovacs64/hibp/tree/main/API.md#search)
79+
- [subscriptionStatus](https://github.com/wKovacs64/hibp/tree/main/API.md#subscriptionstatus)
7880

7981
Please see the
8082
[API reference](https://github.com/wKovacs64/hibp/tree/main/API.md) for more

scripts/fix-api-docs.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ import fs from 'node:fs';
33
const filename = 'API.md';
44
const generatedApiDocs = fs.readFileSync(filename, 'utf8');
55
const newApiDocs = generatedApiDocs
6-
// Replace the generated link to the `Breach` object with a more specific
7-
// anchor name as the generated one collides with the `breach` function
8-
// because anchor links are case insensitive.
6+
// Replace the generated links to certain objects with more specific anchor
7+
// names as the generated ones collide with functions that share a name with
8+
// the objects (as anchor links are case insensitive).
99
.replace(/#Breach/g, '#breach--object')
10+
.replace(/#SubscriptionStatus/g, '#subscriptionstatus--object')
1011
// Surround all the generated Promise.<Array.<Type>> strings with links to the
1112
// corresponding typedef object as jsdoc2md seems to have an issue parsing the
1213
// syntax for a promise that resolves to an array of custom types.

src/__tests__/hibp.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ describe('hibp', () => {
1313
"pwnedPassword": [Function],
1414
"pwnedPasswordRange": [Function],
1515
"search": [Function],
16+
"subscriptionStatus": [Function],
1617
}
1718
`);
1819
});
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { http } from 'msw';
2+
import { server } from '../mocks/server.js';
3+
import { EXAMPLE_SUBSCRIPTION_STATUS } from '../../test/fixtures.js';
4+
import { subscriptionStatus } from '../subscription-status.js';
5+
6+
describe('subscriptionStatus', () => {
7+
const apiKey = 'my-api-key';
8+
9+
describe('apiKey parameter', () => {
10+
it('sets the hibp-api-key header', async () => {
11+
expect.assertions(1);
12+
server.use(
13+
http.get('*', ({ request }) => {
14+
expect(request.headers.get('hibp-api-key')).toBe(apiKey);
15+
return new Response(JSON.stringify(EXAMPLE_SUBSCRIPTION_STATUS));
16+
}),
17+
);
18+
19+
return subscriptionStatus(apiKey);
20+
});
21+
});
22+
23+
describe('baseUrl option', () => {
24+
it('is the beginning of the final URL', () => {
25+
const baseUrl = 'https://my-hibp-proxy:8080';
26+
server.use(
27+
http.get(new RegExp(`^${baseUrl}`), () => {
28+
return new Response(JSON.stringify(EXAMPLE_SUBSCRIPTION_STATUS));
29+
}),
30+
);
31+
32+
return expect(subscriptionStatus(apiKey, { baseUrl })).resolves.toEqual(
33+
EXAMPLE_SUBSCRIPTION_STATUS,
34+
);
35+
});
36+
});
37+
38+
describe('userAgent option', () => {
39+
it('is passed on as a request header', () => {
40+
expect.assertions(1);
41+
const userAgent = 'Custom UA';
42+
server.use(
43+
http.get('*', ({ request }) => {
44+
expect(request.headers.get('User-Agent')).toBe(userAgent);
45+
return new Response(JSON.stringify(EXAMPLE_SUBSCRIPTION_STATUS));
46+
}),
47+
);
48+
49+
return subscriptionStatus(apiKey, { userAgent });
50+
});
51+
});
52+
});

src/api/haveibeenpwned/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ export interface Paste {
3030
EmailCount: number;
3131
}
3232

33+
export interface SubscriptionStatus {
34+
SubscriptionName: string;
35+
Description: string;
36+
SubscribedUntil: string;
37+
Rpm: number;
38+
DomainSearchMaxBreachedAccounts: number;
39+
}
40+
3341
//
3442
// Internal convenience types
3543
//
@@ -44,6 +52,7 @@ export type ApiData =
4452
| Breach[] // breachedaccount, breaches
4553
| Paste[] // pasteaccount
4654
| string[] // dataclasses
55+
| SubscriptionStatus // subscription/status
4756
| null; // most endpoints can return an empty response (404, but not an error)
4857

4958
/**

src/hibp.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { pasteAccount } from './paste-account.js';
77
import { pwnedPassword } from './pwned-password.js';
88
import { pwnedPasswordRange } from './pwned-password-range.js';
99
import { search } from './search.js';
10+
import { subscriptionStatus } from './subscription-status.js';
1011

1112
/*
1213
* Export individual named functions to allow the following:
@@ -26,6 +27,7 @@ export {
2627
pwnedPassword,
2728
pwnedPasswordRange,
2829
search,
30+
subscriptionStatus,
2931
RateLimitError,
3032
};
3133

@@ -40,6 +42,7 @@ export interface HIBP {
4042
pwnedPassword: typeof pwnedPassword;
4143
pwnedPasswordRange: typeof pwnedPasswordRange;
4244
search: typeof search;
45+
subscriptionStatus: typeof subscriptionStatus;
4346
RateLimitError: typeof RateLimitError;
4447
}
4548

0 commit comments

Comments
 (0)