@emulates/aha 0.0.0-stage → 3.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.
package/openapi.yaml ADDED
@@ -0,0 +1,384 @@
1
+ openapi: 3.1.0
2
+ info:
3
+ title: AHA (Advanced Health Academy) partner API (Emulates subset)
4
+ description: |
5
+ Stateful mock of the AHA at-home phlebotomy partner API: create-or-update order and cancel.
6
+ The vendor has no pull API; order state reaches the partner only as inbound webhooks, which
7
+ the mock emits from admin transitions (see the README). Hand-authored from the consumer's
8
+ wire shapes (the vendor publishes no spec).
9
+ version: "1"
10
+ x-emulates-upstream:
11
+ note: >-
12
+ Hand-authored from aha.service.ts, aha.types.ts (zod request/response schemas) and
13
+ aha.lab-provider.ts. Success responses come in one of two envelopes (raw, or
14
+ {success, data}) because our two clients disagree (G-A1); the mock serves either.
15
+ servers:
16
+ - url: https://stage-api.mobileaha.com
17
+ security:
18
+ - hmacKey: []
19
+ hmacTimestamp: []
20
+ hmacSignature: []
21
+ - legacyKey: []
22
+ paths:
23
+ /v1/{partner}/create-order:
24
+ post:
25
+ operationId: CreateOrder
26
+ description: >-
27
+ Create an order, or update it when partner_order_id already exists (the same
28
+ order_number is returned). Honors X-Idempotency-Key.
29
+ x-emulates:
30
+ supported: true
31
+ parity:
32
+ enabled: true
33
+ safe: false
34
+ parameters:
35
+ - $ref: "#/components/parameters/Partner"
36
+ - $ref: "#/components/parameters/IdempotencyKey"
37
+ requestBody:
38
+ required: true
39
+ content:
40
+ application/json:
41
+ schema:
42
+ $ref: "#/components/schemas/CreateOrderBody"
43
+ responses:
44
+ "200":
45
+ description: Order created or updated (raw or wrapped envelope, per namespace setting)
46
+ content:
47
+ application/json:
48
+ schema:
49
+ oneOf:
50
+ - $ref: "#/components/schemas/CreateOrderResponse"
51
+ - $ref: "#/components/schemas/WrappedCreateOrderResponse"
52
+ "400":
53
+ description: Invalid request body
54
+ content:
55
+ application/json:
56
+ schema:
57
+ $ref: "#/components/schemas/ErrorBody"
58
+ "401":
59
+ description: Missing or invalid authentication
60
+ content:
61
+ application/json:
62
+ schema:
63
+ $ref: "#/components/schemas/ErrorBody"
64
+ "409":
65
+ description: Idempotency key reused with a different request, or still in flight
66
+ content:
67
+ application/json:
68
+ schema:
69
+ $ref: "#/components/schemas/ErrorBody"
70
+ "429":
71
+ description: Rate limited
72
+ content:
73
+ application/json:
74
+ schema:
75
+ $ref: "#/components/schemas/ErrorBody"
76
+ "500":
77
+ description: Server error
78
+ content:
79
+ application/json:
80
+ schema:
81
+ $ref: "#/components/schemas/ErrorBody"
82
+ /v1/{partner}/cancel:
83
+ post:
84
+ operationId: CancelOrder
85
+ description: >-
86
+ Cancel an order by partner_order_id. The mock also accepts the AHA order_number here,
87
+ because our lab-provider client sends it in that field (G-A1).
88
+ x-emulates:
89
+ supported: true
90
+ parity:
91
+ enabled: true
92
+ safe: false
93
+ parameters:
94
+ - $ref: "#/components/parameters/Partner"
95
+ - $ref: "#/components/parameters/IdempotencyKey"
96
+ requestBody:
97
+ required: true
98
+ content:
99
+ application/json:
100
+ schema:
101
+ $ref: "#/components/schemas/CancelOrderBody"
102
+ responses:
103
+ "200":
104
+ description: Cancel outcome (raw or wrapped envelope; status ERROR when not cancellable)
105
+ content:
106
+ application/json:
107
+ schema:
108
+ oneOf:
109
+ - $ref: "#/components/schemas/CancelOrderResponse"
110
+ - $ref: "#/components/schemas/WrappedCancelOrderResponse"
111
+ "400":
112
+ description: Invalid request body
113
+ content:
114
+ application/json:
115
+ schema:
116
+ $ref: "#/components/schemas/ErrorBody"
117
+ "401":
118
+ description: Missing or invalid authentication
119
+ content:
120
+ application/json:
121
+ schema:
122
+ $ref: "#/components/schemas/ErrorBody"
123
+ "404":
124
+ description: Unknown partner order
125
+ content:
126
+ application/json:
127
+ schema:
128
+ $ref: "#/components/schemas/ErrorBody"
129
+ "409":
130
+ description: Idempotency key reused with a different request, or still in flight
131
+ content:
132
+ application/json:
133
+ schema:
134
+ $ref: "#/components/schemas/ErrorBody"
135
+ "429":
136
+ description: Rate limited
137
+ content:
138
+ application/json:
139
+ schema:
140
+ $ref: "#/components/schemas/ErrorBody"
141
+ "500":
142
+ description: Server error
143
+ content:
144
+ application/json:
145
+ schema:
146
+ $ref: "#/components/schemas/ErrorBody"
147
+ components:
148
+ securitySchemes:
149
+ hmacKey:
150
+ type: apiKey
151
+ in: header
152
+ name: X-API-KEY
153
+ hmacTimestamp:
154
+ type: apiKey
155
+ in: header
156
+ name: X-TIMESTAMP
157
+ description: Unix epoch milliseconds.
158
+ hmacSignature:
159
+ type: apiKey
160
+ in: header
161
+ name: X-SIGNATURE
162
+ description: base64(HMAC-SHA256(apiSecret, "<apiKey>:<path>:<timestamp>")).
163
+ legacyKey:
164
+ type: apiKey
165
+ in: header
166
+ name: X-Acme-Auth-Key
167
+ description: >-
168
+ Legacy mode (AHA_USE_LEGACY_AUTH=true), sent with X-API-Version 1.0. The real header
169
+ embeds the partner's name (`X-<Partner>-Auth-Key`); the mock accepts any partner name.
170
+ parameters:
171
+ Partner:
172
+ name: partner
173
+ in: path
174
+ required: true
175
+ description: The partner segment AHA assigns your account (its slug), e.g. `acme`.
176
+ schema:
177
+ type: string
178
+ pattern: "^[A-Za-z0-9_-]{1,64}$"
179
+ examples: [acme]
180
+ IdempotencyKey:
181
+ name: X-Idempotency-Key
182
+ in: header
183
+ required: false
184
+ description: Sent by the lab-provider client; the same key and body replays the stored response.
185
+ schema:
186
+ type: string
187
+ pattern: "^[A-Za-z0-9:_.-]{1,128}$"
188
+ schemas:
189
+ TestCode:
190
+ type: object
191
+ required: [test_code, test_description]
192
+ properties:
193
+ test_code:
194
+ type: string
195
+ minLength: 1
196
+ maxLength: 64
197
+ test_description:
198
+ type: string
199
+ maxLength: 200
200
+ CreateOrderBody:
201
+ type: object
202
+ required:
203
+ - partner_order_id
204
+ - patient_first_name
205
+ - patient_id
206
+ - patient_last_name
207
+ - biological_sex
208
+ - patient_dob
209
+ - patient_phone_number
210
+ - patient_address_line1
211
+ - patient_city
212
+ - patient_state
213
+ - patient_zipcode
214
+ - service_type
215
+ - npi
216
+ - ordering_physician
217
+ - test_codes
218
+ properties:
219
+ partner_order_id:
220
+ type: string
221
+ minLength: 1
222
+ maxLength: 64
223
+ patient_first_name:
224
+ type: string
225
+ minLength: 1
226
+ maxLength: 60
227
+ patient_id:
228
+ type: string
229
+ minLength: 1
230
+ maxLength: 64
231
+ patient_middle_initial:
232
+ type: string
233
+ maxLength: 5
234
+ patient_last_name:
235
+ type: string
236
+ minLength: 1
237
+ maxLength: 60
238
+ biological_sex:
239
+ type: string
240
+ enum: [Male, Female, Non-binary]
241
+ patient_dob:
242
+ type: string
243
+ description: YYYY-MM-DD
244
+ maxLength: 10
245
+ patient_phone_number:
246
+ type: string
247
+ description: 10 digits from AhaService; the lab-provider client may send +1XXXXXXXXXX.
248
+ maxLength: 20
249
+ patient_email_address:
250
+ type: string
251
+ maxLength: 120
252
+ patient_address_line1:
253
+ type: string
254
+ minLength: 1
255
+ maxLength: 120
256
+ patient_address_line2:
257
+ type: string
258
+ maxLength: 120
259
+ patient_city:
260
+ type: string
261
+ minLength: 1
262
+ maxLength: 60
263
+ patient_state:
264
+ type: string
265
+ minLength: 1
266
+ maxLength: 20
267
+ patient_zipcode:
268
+ type: string
269
+ minLength: 1
270
+ maxLength: 10
271
+ service_type:
272
+ type: string
273
+ enum: [Full Service, Draw Only, Pickup Only]
274
+ preferred_schedule_date:
275
+ type: string
276
+ description: YYYY-MM-DD
277
+ maxLength: 10
278
+ preferred_schedule_time:
279
+ type: string
280
+ description: HH:MM
281
+ maxLength: 8
282
+ patient_timezone:
283
+ type: string
284
+ description: IANA zone, e.g. America/New_York
285
+ maxLength: 64
286
+ npi:
287
+ type: string
288
+ minLength: 1
289
+ maxLength: 20
290
+ ordering_physician:
291
+ type: string
292
+ minLength: 1
293
+ maxLength: 120
294
+ test_codes:
295
+ type: array
296
+ maxItems: 20
297
+ items:
298
+ $ref: "#/components/schemas/TestCode"
299
+ CancelOrderBody:
300
+ type: object
301
+ required: [partner_order_id, notes]
302
+ properties:
303
+ partner_order_id:
304
+ type: string
305
+ minLength: 1
306
+ maxLength: 64
307
+ x-emulates-resource-ref:
308
+ type: order
309
+ missing: AC-999999999
310
+ notes:
311
+ type: array
312
+ maxItems: 5
313
+ items:
314
+ type: object
315
+ required: [note_type, notes]
316
+ properties:
317
+ note_type:
318
+ type: string
319
+ enum: [CANCELLATION]
320
+ notes:
321
+ type: string
322
+ maxLength: 500
323
+ CreateOrderResponse:
324
+ type: object
325
+ required: [content, message, status]
326
+ properties:
327
+ content:
328
+ type: object
329
+ required: [partner_order_id, order_number]
330
+ properties:
331
+ partner_order_id:
332
+ type: string
333
+ x-emulates-resource:
334
+ type: order
335
+ identity: true
336
+ order_number:
337
+ type: string
338
+ x-emulates-volatile:
339
+ kind: id
340
+ message:
341
+ type: string
342
+ status:
343
+ type: string
344
+ enum: [SUCCESS, ERROR]
345
+ WrappedCreateOrderResponse:
346
+ type: object
347
+ required: [success, data]
348
+ properties:
349
+ success:
350
+ type: boolean
351
+ data:
352
+ $ref: "#/components/schemas/CreateOrderResponse"
353
+ CancelOrderResponse:
354
+ type: object
355
+ required: [message, status]
356
+ properties:
357
+ message:
358
+ type: string
359
+ status:
360
+ type: string
361
+ enum: [SUCCESS, ERROR]
362
+ WrappedCancelOrderResponse:
363
+ type: object
364
+ required: [success, data]
365
+ properties:
366
+ success:
367
+ type: boolean
368
+ data:
369
+ $ref: "#/components/schemas/CancelOrderResponse"
370
+ ErrorBody:
371
+ type: object
372
+ required: [status, message]
373
+ properties:
374
+ status:
375
+ type: string
376
+ enum: [ERROR]
377
+ message:
378
+ type: string
379
+ errors:
380
+ type: object
381
+ additionalProperties:
382
+ type: array
383
+ items:
384
+ type: string
package/package.json CHANGED
@@ -1,6 +1,128 @@
1
1
  {
2
2
  "name": "@emulates/aha",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "3.0.0",
4
+ "description": "Stateful AHA phlebotomy emulator with signed HTTP orders, status webhooks, and a real deterministic SSH/SFTP result server.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "files": [
9
+ "dist",
10
+ "README.md",
11
+ "CHANGELOG.md",
12
+ "DISCOVERY.md",
13
+ "SUPPORT.md",
14
+ "openapi.yaml"
15
+ ],
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
22
+ },
23
+ "./server": {
24
+ "types": "./dist/server.d.ts",
25
+ "default": "./dist/server.js"
26
+ },
27
+ "./sftp": {
28
+ "types": "./dist/sftp.d.ts",
29
+ "default": "./dist/sftp.js"
30
+ }
31
+ },
32
+ "bin": {
33
+ "emulates-aha": "./dist/cli.js"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public",
37
+ "provenance": true
38
+ },
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/crvouga/emulates.git",
42
+ "directory": "packages/service/aha"
43
+ },
44
+ "homepage": "https://github.com/crvouga/emulates/tree/main/packages/service/aha#readme",
45
+ "keywords": [
46
+ "emulates",
47
+ "service",
48
+ "aha",
49
+ "phlebotomy",
50
+ "bloodwork",
51
+ "lab",
52
+ "webhooks",
53
+ "sftp"
54
+ ],
55
+ "emulates": {
56
+ "runtime": "portable",
57
+ "entries": {
58
+ "server": "node",
59
+ "cli": "node",
60
+ "sftp": "node"
61
+ },
62
+ "layer": "service",
63
+ "category": "health",
64
+ "displayName": "AHA",
65
+ "vendor": {
66
+ "website": "https://www.mobileaha.com",
67
+ "name": "mobileAHA",
68
+ "description": "mobileAHA provides mobile phlebotomy: at-home and workplace blood draws for lab orders."
69
+ },
70
+ "parity": "Phlebotomy orders and result files",
71
+ "parityTier": "cold",
72
+ "discovery": {
73
+ "guide": "DISCOVERY.md",
74
+ "behavior": "README.md",
75
+ "capabilities": "SUPPORT.md",
76
+ "contract": "openapi.yaml",
77
+ "types": "dist/index.d.ts",
78
+ "oracle": {
79
+ "kind": "Live vendor API or sandbox",
80
+ "command": "bun run parity:service -- aha",
81
+ "note": "Run from an Emulates checkout; credentials come only from .env.local or GitHub Actions secrets. Missing credentials exit 2."
82
+ },
83
+ "introspection": [
84
+ "GET /__admin/health",
85
+ "GET /__admin",
86
+ "GET /__admin/state",
87
+ "GET /__admin/requests",
88
+ "GET /__admin/metrics",
89
+ "GET /__admin/faults/presets",
90
+ "GET /__admin/ui"
91
+ ],
92
+ "reporting": "https://github.com/crvouga/emulates/blob/main/docs/REPORTING_ISSUES.md"
93
+ }
94
+ },
95
+ "scripts": {
96
+ "generate": "bun ../../openapi/codegen/dist/cli.js",
97
+ "generate:check": "bun ../../openapi/codegen/dist/cli.js --check",
98
+ "openapi:check": "bun ../../openapi/codegen/dist/cli.js --validate",
99
+ "build": "bun ../../../scripts/bundle-service.ts",
100
+ "typecheck": "tsc -p tsconfig.json --noEmit",
101
+ "lint": "biome check .",
102
+ "test": "bun test",
103
+ "parity": "bun scripts/parity.ts",
104
+ "portability": "bun ../../../scripts/portability.ts",
105
+ "pack:check": "bun ../../../scripts/pack-check.ts"
106
+ },
107
+ "dependencies": {
108
+ "@emulates/sqlite": "3.0.0",
109
+ "hono": "4.11.9",
110
+ "ssh2": "1.17.0"
111
+ },
112
+ "devDependencies": {
113
+ "@emulates/adapter-node": "0.0.0-development",
114
+ "@emulates/core": "0.0.0-development",
115
+ "@emulates/http-codec": "0.0.0-development",
116
+ "@emulates/openapi": "0.0.0-development",
117
+ "@emulates/openapi-codegen": "0.1.0",
118
+ "@emulates/openapi-metadata": "0.0.0-development",
119
+ "@emulates/credentials": "0.0.0-development",
120
+ "@emulates/parity": "0.0.0-development",
121
+ "@emulates/service": "0.0.0-development",
122
+ "@emulates/sqlite": "0.0.0-development",
123
+ "@emulates/sqlite-client": "0.0.0-development",
124
+ "@emulates/testing": "0.1.0",
125
+ "@types/ssh2": "1.15.6",
126
+ "fast-check": "4.9.0"
127
+ }
128
+ }