@hello-bill/node 1.0.0 → 1.0.1

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 (50) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/LICENSE +21 -0
  3. package/README.md +216 -0
  4. package/dist/express/index.d.cts +34 -0
  5. package/dist/express/index.d.ts +34 -0
  6. package/dist/express/index.js +1019 -0
  7. package/dist/express/index.js.map +1 -0
  8. package/dist/express/index.mjs +1016 -0
  9. package/dist/express/index.mjs.map +1 -0
  10. package/dist/hono/index.d.cts +37 -0
  11. package/dist/hono/index.d.ts +37 -0
  12. package/dist/hono/index.js +1036 -0
  13. package/dist/hono/index.js.map +1 -0
  14. package/dist/hono/index.mjs +1033 -0
  15. package/dist/hono/index.mjs.map +1 -0
  16. package/dist/index-BrfG82zs.d.cts +341 -0
  17. package/dist/index-EXetQn1f.d.ts +341 -0
  18. package/dist/index.d.cts +164 -0
  19. package/dist/index.d.ts +164 -0
  20. package/dist/index.js +1178 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/index.mjs +1164 -0
  23. package/dist/index.mjs.map +1 -0
  24. package/dist/koa/index.d.cts +42 -0
  25. package/dist/koa/index.d.ts +42 -0
  26. package/dist/koa/index.js +1042 -0
  27. package/dist/koa/index.js.map +1 -0
  28. package/dist/koa/index.mjs +1039 -0
  29. package/dist/koa/index.mjs.map +1 -0
  30. package/dist/next/index.d.cts +60 -0
  31. package/dist/next/index.d.ts +60 -0
  32. package/dist/next/index.js +1092 -0
  33. package/dist/next/index.js.map +1 -0
  34. package/dist/next/index.mjs +1090 -0
  35. package/dist/next/index.mjs.map +1 -0
  36. package/dist/types/index.d.cts +51 -0
  37. package/dist/types/index.d.ts +51 -0
  38. package/dist/types/index.js +12 -0
  39. package/dist/types/index.js.map +1 -0
  40. package/dist/types/index.mjs +9 -0
  41. package/dist/types/index.mjs.map +1 -0
  42. package/dist/webhook/index.d.cts +2 -0
  43. package/dist/webhook/index.d.ts +2 -0
  44. package/dist/webhook/index.js +342 -0
  45. package/dist/webhook/index.js.map +1 -0
  46. package/dist/webhook/index.mjs +336 -0
  47. package/dist/webhook/index.mjs.map +1 -0
  48. package/dist/webhooks-DPpqf7d2.d.cts +751 -0
  49. package/dist/webhooks-DPpqf7d2.d.ts +751 -0
  50. package/package.json +164 -7
package/CHANGELOG.md ADDED
@@ -0,0 +1,33 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@hello-bill/node` will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.0.1] - 2026-05-16
9
+
10
+ ### Added
11
+
12
+ - Initial pre-release surface of the HelloBill Partner API SDK.
13
+ - `core` entry (`@hello-bill/node`): framework-agnostic
14
+ `createHellobillHandler` returning a handler set (`init`, `session`,
15
+ `loa`, `customer`, callbacks), plus `TokenManager`, `InMemoryTokenStorage`,
16
+ retry/idempotency helpers, and typed error classes.
17
+ - `express` entry (`@hello-bill/node/express`): `createHellobillRouter`
18
+ Express 4/5 adapter (peer-dep, optional) that wires the handler set onto
19
+ an `express.Router`.
20
+ - `webhook` entry (`@hello-bill/node/webhook`): `verifyWebhook` with
21
+ `WebhookVerificationError` and timing-safe HMAC checks; raw-body verifier
22
+ matching the Partner API signature header format.
23
+ - `types` entry (`@hello-bill/node/types`): spec-derived TypeScript surface
24
+ for Partner API v11 (sessions, products, LOA, customers, webhooks,
25
+ envelopes, error shapes).
26
+ - ESM + CJS dual build via `tsup` with per-entry `.d.ts` declarations and
27
+ sourcemaps. ESM-first; CJS provided for server-side consumers on older
28
+ bundlers.
29
+
30
+ ### Notes
31
+
32
+ - `0.0.x` releases are pre-release and may change without notice. Promote
33
+ to `0.1.0` once the public surface is frozen.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 The Bunch
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,216 @@
1
+ # @hello-bill/node
2
+
3
+ Server-side SDK for the **HelloBill Partner API (Revision 1.13)** (`api_version 2026-05-10`).
4
+ Framework-agnostic core with an Express adapter; ships a webhook verifier and
5
+ the spec-derived TypeScript types.
6
+
7
+ - ESM only, bare TypeScript source — no runtime dependencies.
8
+ - Subpath imports: `@hello-bill/node/express`, `@hello-bill/node/webhook`, `@hello-bill/node/types`.
9
+ - Auto OAuth token caching, single-flight refresh, idempotency, retry with
10
+ `Retry-After`, per-attempt timeout.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install @hello-bill/node
16
+ # or
17
+ bun add @hello-bill/node
18
+ ```
19
+
20
+ > The package is currently `private: true` and consumed via the monorepo
21
+ > workspace. See **Pre-publish checklist** below.
22
+
23
+ ## Quick start (Express)
24
+
25
+ ```ts
26
+ import express from 'express';
27
+ import { createHellobillRouter } from '@hello-bill/node/express';
28
+
29
+ const app = express();
30
+
31
+ app.use('/api/hellobill', createHellobillRouter({
32
+ clientId: process.env.HELLOBILL_CLIENT_ID!,
33
+ clientSecret: process.env.HELLOBILL_CLIENT_SECRET!,
34
+ apiBaseUrl: process.env.HELLOBILL_API_BASE_URL!,
35
+ buildSessionPayload: async (req) => {
36
+ // Map whatever your host page posted → canonical snake_case SessionPayload.
37
+ const body = req.body as { firstName: string; lastName: string; email: string };
38
+ return {
39
+ customer: {
40
+ first_name: body.firstName,
41
+ last_name: body.lastName,
42
+ email: body.email,
43
+ },
44
+ addresses: { current: { /* ... */ } },
45
+ move: { in: { move_in_date: '2026-06-01' } },
46
+ consent: { data_sharing_accepted: true },
47
+ };
48
+ },
49
+ }));
50
+
51
+ app.listen(3000);
52
+ ```
53
+
54
+ The router applies `express.json({ limit: '1mb' })` internally — do not wrap
55
+ it with another body parser.
56
+
57
+ ## Endpoints mounted
58
+
59
+ `createHellobillRouter(opts)` mounts these paths under the prefix you choose:
60
+
61
+ | Method | Path | Upstream |
62
+ |--------|--------------|-----------------------------------------------------|
63
+ | POST | `/session` | `POST /partner/sessions` |
64
+ | GET | `/products` | `GET /partner/sessions/:session_id/products` |
65
+ | GET | `/loa` | `GET /partner/sessions/:session_id/loa` |
66
+ | POST | `/customers` | `POST /partner/sessions/:session_id/customers` |
67
+ | GET | `/status` | `GET /partner/customers/:customer_id/status` |
68
+
69
+ `/products`, `/loa`, `/customers` and `/status` require a session_token
70
+ Bearer (issued from `POST /session`). The SDK decodes it without verification
71
+ to extract `session_id` and forwards the upstream call with a fresh OAuth
72
+ access token.
73
+
74
+ ## Framework-agnostic core
75
+
76
+ Partners on Hono / Koa / Fastify / Nitro / raw Node can skip the Express
77
+ adapter and use the core directly:
78
+
79
+ ```ts
80
+ import { createHellobillHandler } from '@hello-bill/node';
81
+
82
+ const handlers = createHellobillHandler({
83
+ clientId: '…', clientSecret: '…', apiBaseUrl: '…',
84
+ buildSessionPayload: async (req) => ({ /* … */ }),
85
+ });
86
+
87
+ // Each handler takes the SDK's portable HellobillRequest / HellobillResponse.
88
+ // You write the ~15-line adapter that shapes your framework's req/res into them.
89
+ await handlers.session(req, res);
90
+ ```
91
+
92
+ ## Webhook verification
93
+
94
+ ```ts
95
+ import express from 'express';
96
+ import { verifyWebhook, WebhookVerificationError } from '@hello-bill/node/webhook';
97
+
98
+ app.post(
99
+ '/api/hellobill/webhooks',
100
+ express.raw({ type: 'application/json' }), // ← raw bytes, not parsed JSON
101
+ (req, res) => {
102
+ try {
103
+ const event = verifyWebhook(
104
+ req.body,
105
+ req.header('x-hellobill-signature')!,
106
+ process.env.HELLOBILL_WEBHOOK_SECRET!,
107
+ { tolerance: 300, supportedVersions: ['v1'] },
108
+ );
109
+ // event is typed as WebhookEvent — switch on event.type
110
+ res.status(204).end();
111
+ } catch (err) {
112
+ if (err instanceof WebhookVerificationError) return res.status(400).end();
113
+ throw err;
114
+ }
115
+ },
116
+ );
117
+ ```
118
+
119
+ - **Algorithm**: HMAC-SHA256 over `${unix_timestamp}.${raw_body}` (Stripe-style).
120
+ - **Header format**: `t=<unix>,v1=<hex>` (and optionally `v2=<hex>` during rotation).
121
+ - **Replay window**: 300s by default (`tolerance`).
122
+ - **Key rotation**: pass `supportedVersions: ['v1', 'v2']` and accept either; cut over once `v2` is verified end-to-end.
123
+ - **Constant-time compare**: implemented via `crypto.timingSafeEqual`.
124
+
125
+ **Never** parse and re-stringify the body before verification — the HMAC is
126
+ computed over the exact bytes the server sent.
127
+
128
+ ## Token management
129
+
130
+ - Automatic `client_credentials` OAuth grant against `POST /auth/partner/token`.
131
+ - Cached in-process by default; refreshed at `expires_in − 30s`.
132
+ - **Single-flight**: parallel API calls during a refresh share one upstream request.
133
+ - Pluggable via `tokenStorage`:
134
+
135
+ ```ts
136
+ import type { TokenStorage } from '@hello-bill/node';
137
+
138
+ const redisStorage: TokenStorage = {
139
+ async get() { /* return { accessToken, expiresAt } | null */ },
140
+ async set(tok) { /* persist */ },
141
+ };
142
+
143
+ createHellobillRouter({ /* …, */ tokenStorage: redisStorage });
144
+ ```
145
+
146
+ ## Retry policy
147
+
148
+ Applied to every upstream call:
149
+
150
+ - **Retried statuses**: `429`, `500`, `502`, `503`, `504`, plus
151
+ `auth.token_expired` (refreshes the token then retries once).
152
+ - **Backoff**: exponential with jitter; honours `Retry-After` (seconds or HTTP date).
153
+ - **Attempts**: up to 5 per call.
154
+ - **Timeout**: per-attempt, default 30 000 ms; override with `upstreamTimeoutMs`.
155
+ - **Network failures**: a hung / refused / aborted fetch becomes a clean
156
+ `internal.error` envelope (502/504 depending on failure kind).
157
+
158
+ ## Idempotency
159
+
160
+ - The SDK auto-generates a UUID v4 `Idempotency-Key` for every POST.
161
+ - Override with `idempotencyKeyFactory: () => string`.
162
+
163
+ **Gotcha**: the Partner API caches the first response keyed by `Idempotency-Key`
164
+ *and ignores the body on subsequent requests with the same key*. If you reuse
165
+ the same key for two semantically-different POSTs (e.g. two different customer
166
+ payloads), you get the **first** response back — not an error. Partners must
167
+ mint a distinct key per logical operation.
168
+
169
+ ## Types
170
+
171
+ The full spec surface is re-exported under a stable subpath:
172
+
173
+ ```ts
174
+ import type {
175
+ SessionPayload,
176
+ SessionResponse,
177
+ ProductsResponse,
178
+ LoaResponse,
179
+ CustomersPayload,
180
+ CustomersResponse,
181
+ StatusResponse,
182
+ WebhookEvent,
183
+ } from '@hello-bill/node/types';
184
+ ```
185
+
186
+ ## Coverage
187
+
188
+ Measured with `bun run test:coverage` (vitest + v8). 69 / 69 tests passing.
189
+
190
+ | Area | Statements | Branches | Functions | Lines |
191
+ |-----------|------------|----------|-----------|--------|
192
+ | **All** | 90.90 % | 84.95 % | 96.49 % | 94.70 % |
193
+ | `core/` | 91.63 % | 85.02 % | 97.43 % | 95.79 % |
194
+ | `express/`| 83.33 % | n/a | 90.90 % | 86.20 % |
195
+ | `webhook/`| 90.90 % | 89.18 % | 100.00 % | 94.11 % |
196
+
197
+ Uncovered lines are exhaustively defensive branches (e.g. unreachable
198
+ `else` in error envelope shaping, top-level Express error middleware that
199
+ fires only on developer mis-wiring).
200
+
201
+ ## Pre-publish checklist
202
+
203
+ Before the first `npm publish`:
204
+
205
+ - [ ] Remove `"private": true` from `package.json`.
206
+ - [ ] Confirm the `"files"` field (currently `["src", "README.md"]`) — add a `dist/` if you ship compiled output.
207
+ - [ ] Add a build step (`tsup` or `tsc`) and point `"types"` at the emitted `.d.ts`.
208
+ - [ ] Verify all `"exports"` subpaths resolve post-build (`.`, `/express`, `/webhook`, `/types`).
209
+ - [ ] Choose a licence + add `LICENSE` file.
210
+ - [ ] Add `"repository"` and `"homepage"` fields.
211
+ - [ ] Seed `CHANGELOG.md` (recommend Keep a Changelog).
212
+ - [ ] Run `npm pack --dry-run` and review the tarball contents.
213
+
214
+ ## License
215
+
216
+ TBD.
@@ -0,0 +1,34 @@
1
+ import { C as CreateHellobillHandlerOptions } from '../index-BrfG82zs.cjs';
2
+ import '../webhooks-DPpqf7d2.cjs';
3
+
4
+ /**
5
+ * Express adapter for the framework-agnostic SDK core.
6
+ *
7
+ * Usage:
8
+ * import { createHellobillRouter } from '@hello-bill/node/express';
9
+ * app.use('/api/hellobill', createHellobillRouter({ ... }));
10
+ *
11
+ * `express` is an OPTIONAL peer dependency — it's loaded lazily via `require`
12
+ * so consumers that never import this adapter pay nothing.
13
+ */
14
+
15
+ type AnyRouter = any;
16
+ /**
17
+ * Creates an Express Router that mounts the HelloBill endpoints:
18
+ * POST /webhooks (optional — only when opts.webhook is provided)
19
+ * POST /session
20
+ * GET /loa
21
+ * POST /customers
22
+ * GET /status
23
+ * GET /products
24
+ * GET /property
25
+ * POST /bank-details
26
+ * GET /bank-details
27
+ * GET /sessions (additive — spec §3.6 + §9.2.1)
28
+ * GET /sessions/:id (additive — spec §3.7 + §9.2.1)
29
+ *
30
+ * Mount it under a path of your choice, e.g. `/api/hellobill`.
31
+ */
32
+ declare function createHellobillRouter(opts: CreateHellobillHandlerOptions): AnyRouter;
33
+
34
+ export { createHellobillRouter };
@@ -0,0 +1,34 @@
1
+ import { C as CreateHellobillHandlerOptions } from '../index-EXetQn1f.js';
2
+ import '../webhooks-DPpqf7d2.js';
3
+
4
+ /**
5
+ * Express adapter for the framework-agnostic SDK core.
6
+ *
7
+ * Usage:
8
+ * import { createHellobillRouter } from '@hello-bill/node/express';
9
+ * app.use('/api/hellobill', createHellobillRouter({ ... }));
10
+ *
11
+ * `express` is an OPTIONAL peer dependency — it's loaded lazily via `require`
12
+ * so consumers that never import this adapter pay nothing.
13
+ */
14
+
15
+ type AnyRouter = any;
16
+ /**
17
+ * Creates an Express Router that mounts the HelloBill endpoints:
18
+ * POST /webhooks (optional — only when opts.webhook is provided)
19
+ * POST /session
20
+ * GET /loa
21
+ * POST /customers
22
+ * GET /status
23
+ * GET /products
24
+ * GET /property
25
+ * POST /bank-details
26
+ * GET /bank-details
27
+ * GET /sessions (additive — spec §3.6 + §9.2.1)
28
+ * GET /sessions/:id (additive — spec §3.7 + §9.2.1)
29
+ *
30
+ * Mount it under a path of your choice, e.g. `/api/hellobill`.
31
+ */
32
+ declare function createHellobillRouter(opts: CreateHellobillHandlerOptions): AnyRouter;
33
+
34
+ export { createHellobillRouter };