@astermd-hq/sdk 0.0.2
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/LICENSE +21 -0
- package/README.md +253 -0
- package/dist/index.cjs +3338 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2825 -0
- package/dist/index.d.ts +2825 -0
- package/dist/index.js +3298 -0
- package/dist/index.js.map +1 -0
- package/package.json +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AsterMD
|
|
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,253 @@
|
|
|
1
|
+
# AsterMD Node.js SDK
|
|
2
|
+
|
|
3
|
+
Slim, framework-agnostic TypeScript SDK for the AsterMD order-flow API.
|
|
4
|
+
|
|
5
|
+
- Node `>=22`
|
|
6
|
+
- **Zero runtime dependencies** - the platform `fetch`, `node:crypto`, and `node:fs` only
|
|
7
|
+
- Dual ESM + CJS builds with full type declarations
|
|
8
|
+
- Bring your own HTTP client if you prefer
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @astermd-hq/sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { AsterMDClient, Event } from '@astermd-hq/sdk';
|
|
20
|
+
|
|
21
|
+
const client = new AsterMDClient({
|
|
22
|
+
clientId: process.env.ASTERMD_CLIENT_ID!,
|
|
23
|
+
clientSecret: process.env.ASTERMD_CLIENT_SECRET!,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// 1. Start a session (implicit `visit_page` event server-side)
|
|
27
|
+
const { session } = (await client.sessions().create<{ session: string }>()).data();
|
|
28
|
+
|
|
29
|
+
// 2. Pre-qualifying form
|
|
30
|
+
await client.intakeSubmissions().create({
|
|
31
|
+
session,
|
|
32
|
+
event: Event.PreQualifyingInitiated,
|
|
33
|
+
teleformId: 'your-teleform-id',
|
|
34
|
+
// `data` is a list of field objects: id / name / label / type / value
|
|
35
|
+
data: [{ id: 'name-1', name: 'name', label: 'Full name', type: 'text', value: [{ value: 'Jane' }] }],
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// 3. Create the opportunity, attaching the session
|
|
39
|
+
const opportunity = await client.opportunities().create({
|
|
40
|
+
first_name: 'Jane',
|
|
41
|
+
email: 'jane@example.com',
|
|
42
|
+
sessions: [session],
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// 4. Intake
|
|
46
|
+
await client.intakeSubmissions().update({
|
|
47
|
+
session,
|
|
48
|
+
event: Event.IntakeCompleted,
|
|
49
|
+
teleformId: 'your-teleform-id',
|
|
50
|
+
data: [
|
|
51
|
+
{ id: 'name-1', name: 'name', label: 'Full name', type: 'text', value: [{ value: 'Jane' }] },
|
|
52
|
+
{
|
|
53
|
+
id: 'dob-1',
|
|
54
|
+
name: 'dob',
|
|
55
|
+
label: 'Date of birth',
|
|
56
|
+
type: 'date',
|
|
57
|
+
value: [{ value: '1990-01-15' }],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// 5. After external payment settles, create the treatment (= order)
|
|
63
|
+
await client.treatments().create({
|
|
64
|
+
external_order_id: 'EXT-123',
|
|
65
|
+
patient: { id: '...' },
|
|
66
|
+
product: { id: '...' },
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Credentials are issued from your AsterMD dashboard, which is also where the full
|
|
71
|
+
API reference lives. Load the secret from an environment variable or a secrets
|
|
72
|
+
manager - never commit it, and never ship it to a browser. This SDK is
|
|
73
|
+
server-side only.
|
|
74
|
+
|
|
75
|
+
Construct the client once and share it: it caches the bearer token, so a client
|
|
76
|
+
per request throws that cache away.
|
|
77
|
+
|
|
78
|
+
## Resources
|
|
79
|
+
|
|
80
|
+
`sessions`, `intakeSubmissions`, `carts`, `checkoutEvents`, `teleforms`,
|
|
81
|
+
`patients`, `opportunities`, `treatments`, `doctorsNetworks`, `channels`,
|
|
82
|
+
`products`, `categories`, `labTests`, `medications`, `shippings`,
|
|
83
|
+
`verification`, `geo`.
|
|
84
|
+
|
|
85
|
+
## Errors
|
|
86
|
+
|
|
87
|
+
Every error extends `AsterMDError`:
|
|
88
|
+
|
|
89
|
+
- `TransportError` - network failure, no response received
|
|
90
|
+
- `AuthenticationError` - 401
|
|
91
|
+
- `NotFoundError` - 404
|
|
92
|
+
- `ValidationError` - 422 (`.fieldErrors()`)
|
|
93
|
+
- `RateLimitError` - 429 (`.retryAfter()`)
|
|
94
|
+
- `ApiError` - other 4xx/5xx (`.statusCode()`, `.envelope()`)
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { RateLimitError, ValidationError } from '@astermd-hq/sdk';
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
await client.patients().create({/* ... */});
|
|
101
|
+
} catch (error) {
|
|
102
|
+
if (error instanceof ValidationError) {
|
|
103
|
+
console.error(error.fieldErrors());
|
|
104
|
+
} else if (error instanceof RateLimitError) {
|
|
105
|
+
await new Promise(resolve => setTimeout(resolve, (error.retryAfter() ?? 5) * 1000));
|
|
106
|
+
} else {
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Token storage
|
|
113
|
+
|
|
114
|
+
By default the bearer token is cached in memory for the life of the process,
|
|
115
|
+
which is what a long-running server wants. Where each request runs in a fresh
|
|
116
|
+
process or isolate, that cache is never hit - use `FileTokenStore`, or implement
|
|
117
|
+
`TokenStore` against Redis or any other shared store.
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
import { AsterMDClient, FileTokenStore } from '@astermd-hq/sdk';
|
|
121
|
+
|
|
122
|
+
const client = new AsterMDClient({
|
|
123
|
+
clientId: process.env.ASTERMD_CLIENT_ID!,
|
|
124
|
+
clientSecret: process.env.ASTERMD_CLIENT_SECRET!,
|
|
125
|
+
tokenStore: new FileTokenStore('/var/cache/astermd/token.json'),
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Debugging
|
|
130
|
+
|
|
131
|
+
Pass `debug: true` with a `debugFile` to log every request as a copy-pasteable
|
|
132
|
+
curl command plus its response.
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
const client = new AsterMDClient({
|
|
136
|
+
clientId: process.env.ASTERMD_CLIENT_ID!,
|
|
137
|
+
clientSecret: process.env.ASTERMD_CLIENT_SECRET!,
|
|
138
|
+
debug: true,
|
|
139
|
+
debugFile: '/var/log/astermd/sdk.log',
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Credentials are redacted by default.** Bearer tokens, the client secret, PHI
|
|
144
|
+
verification tokens, and the bodies of `patients/*` calls are replaced with
|
|
145
|
+
`[REDACTED]`:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
curl --location --request POST 'https://api.astermd.com/v1/sales/sessions/create' \
|
|
149
|
+
--header 'authorization: Bearer [REDACTED]' \
|
|
150
|
+
--header 'content-type: application/json' \
|
|
151
|
+
--data '{}'
|
|
152
|
+
|
|
153
|
+
# Response: HTTP 200
|
|
154
|
+
{"success":true,"message":"ok","data":{"session":"..."}}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Pass `debugRedact: false` to log verbatim, including the live JWT. Never do that
|
|
158
|
+
in production. Note that redaction covers headers and bodies but not the URL, so
|
|
159
|
+
endpoints taking their input as a query parameter log that input.
|
|
160
|
+
|
|
161
|
+
### Log rotation
|
|
162
|
+
|
|
163
|
+
`debugFile` is a _base path_. The SDK writes one file per day derived from it and
|
|
164
|
+
deletes files older than `debugRetentionDays` (default 7; `0` keeps everything):
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
/var/log/astermd/sdk-2026-09-08.log
|
|
168
|
+
/var/log/astermd/sdk-2026-09-07.log
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Pruning happens once per sink instance, and only files matching the SDK's own
|
|
172
|
+
`{name}-YYYY-MM-DD.{ext}` pattern are ever removed.
|
|
173
|
+
|
|
174
|
+
### Sending logs somewhere else
|
|
175
|
+
|
|
176
|
+
Supply a `debugSink` and the SDK writes no files at all - retention and delivery
|
|
177
|
+
become yours. This works with any destination: a logger, a hosted log service, a
|
|
178
|
+
cloud provider's logging API, a queue.
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
const client = new AsterMDClient({
|
|
182
|
+
clientId: process.env.ASTERMD_CLIENT_ID!,
|
|
183
|
+
clientSecret: process.env.ASTERMD_CLIENT_SECRET!,
|
|
184
|
+
debug: true,
|
|
185
|
+
debugSink: entry => logger.debug(entry),
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
See [`docs/INTEGRATION_GUIDE.md`](docs/INTEGRATION_GUIDE.md) for worked examples.
|
|
190
|
+
|
|
191
|
+
## Bringing your own HTTP client
|
|
192
|
+
|
|
193
|
+
The SDK talks to one small interface, so anything that can send a `Request` can
|
|
194
|
+
be swapped in - a proxy agent, your own retry policy, a test stub.
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
import { AsterMDClient, type HttpClient } from '@astermd-hq/sdk';
|
|
198
|
+
|
|
199
|
+
class InstrumentedHttpClient implements HttpClient {
|
|
200
|
+
async sendRequest(request: Request): Promise<Response> {
|
|
201
|
+
const started = performance.now();
|
|
202
|
+
try {
|
|
203
|
+
return await fetch(request);
|
|
204
|
+
} finally {
|
|
205
|
+
metrics.timing('astermd.request', performance.now() - started);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const client = new AsterMDClient({
|
|
211
|
+
clientId: process.env.ASTERMD_CLIENT_ID!,
|
|
212
|
+
clientSecret: process.env.ASTERMD_CLIENT_SECRET!,
|
|
213
|
+
httpClient: new InstrumentedHttpClient(),
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Development
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
npm install
|
|
221
|
+
npm run ci # lint → format → typecheck → test → build → publint
|
|
222
|
+
# also: npm test, npm run lint, npm run fix, npm run typecheck, npm run coverage
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
A `Makefile` wraps the same scripts (`make install`, `make ci`, `make test`) if
|
|
226
|
+
you prefer.
|
|
227
|
+
|
|
228
|
+
## Compliance
|
|
229
|
+
|
|
230
|
+
This SDK is a client for an API that carries personal and protected health
|
|
231
|
+
information. Using it does not by itself make your application HIPAA compliant -
|
|
232
|
+
that depends on your own infrastructure, policies, and agreements. Contact
|
|
233
|
+
info@astermd.com regarding a Business Associate Agreement.
|
|
234
|
+
|
|
235
|
+
See [`SECURITY.md`](SECURITY.md) for vulnerability reporting and
|
|
236
|
+
credential-handling guidance.
|
|
237
|
+
|
|
238
|
+
## Further reading
|
|
239
|
+
|
|
240
|
+
| Audience | Document |
|
|
241
|
+
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
242
|
+
| **Integrators building on the SDK** | [`docs/INTEGRATION_GUIDE.md`](docs/INTEGRATION_GUIDE.md) - order flow, resources, auth lifecycle, framework recipes |
|
|
243
|
+
| Contributors | [`CONTRIBUTING.md`](CONTRIBUTING.md) - local setup, how to add an endpoint |
|
|
244
|
+
| Architecture | [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) - single-page overview |
|
|
245
|
+
| Changes | [`CHANGELOG.md`](CHANGELOG.md) |
|
|
246
|
+
|
|
247
|
+
## Support
|
|
248
|
+
|
|
249
|
+
Questions, access requests, and licensing enquiries: **info@astermd.com**
|
|
250
|
+
|
|
251
|
+
## License
|
|
252
|
+
|
|
253
|
+
[MIT](LICENSE) © 2026 AsterMD
|