@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 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