@astermd-hq/sdk 0.0.2 → 0.0.4
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/README.md +1 -1
- package/dist/index.cjs +103 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +179 -14
- package/dist/index.d.ts +179 -14
- package/dist/index.js +103 -7
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/auth/in-memory-token-store.ts","../src/errors/astermd-error.ts","../src/errors/api-error.ts","../src/errors/authentication-error.ts","../src/errors/not-found-error.ts","../src/errors/rate-limit-error.ts","../src/errors/transport-error.ts","../src/errors/validation-error.ts","../src/response.ts","../src/http/transport.ts","../src/auth/token.ts","../src/auth/token-manager.ts","../src/config.ts","../src/http/fetch-http-client.ts","../src/http/log-redactor.ts","../src/http/curl-formatter.ts","../src/http/logging-http-client.ts","../src/http/url-builder.ts","../src/log/daily-file-log-sink.ts","../src/resource/abstract-resource.ts","../src/resource/carts.ts","../src/resource/categories.ts","../src/resource/channels.ts","../src/enum/checkout-event.ts","../src/resource/checkout-events.ts","../src/resource/doctors-networks.ts","../src/resource/geo.ts","../src/http/file-upload.ts","../src/resource/intake-submissions.ts","../src/resource/lab-tests.ts","../src/resource/medications.ts","../src/resource/opportunities.ts","../src/resource/patients.ts","../src/resource/products.ts","../src/resource/sessions.ts","../src/resource/shippings.ts","../src/resource/teleforms.ts","../src/resource/treatments.ts","../src/resource/verification.ts","../src/astermd-client.ts","../src/enum/event.ts","../src/enum/identity-check.ts","../src/auth/file-token-store.ts","../src/presenter/channel-detail.ts","../src/support/query-param-cipher.ts"],"names":["retryAfter","fieldErrors","Response","escapeRegExp","basename","extname","stat","readFile","dirname","join","mkdir","rm"],"mappings":"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","file":"index.js","sourcesContent":["import type { Token } from './token.js';\nimport type { TokenStore } from './token-store.js';\n\n/**\n * The default {@link TokenStore}: keeps the token in memory for the life of the\n * process.\n *\n * Right for a long-running server - an Express or Fastify app, a worker - where\n * one credential exchange serves every request until the token expires. Nothing\n * is written anywhere, so nothing can leak to disk.\n *\n * Wrong where each request runs in a fresh process or isolate, since the cache\n * dies with it and every request pays for its own token exchange. Use\n * {@link FileTokenStore} or your own {@link TokenStore} there.\n */\nexport class InMemoryTokenStore implements TokenStore {\n #token: Token | null = null;\n\n /**\n * Returns the token held in memory, or `null` before the first exchange.\n *\n * @returns The cached token, or `null`.\n */\n get(): Promise<Token | null> {\n return Promise.resolve(this.#token);\n }\n\n /**\n * Replaces the token held in memory.\n *\n * @param token The token to cache.\n */\n put(token: Token): Promise<void> {\n this.#token = token;\n\n return Promise.resolve();\n }\n\n /**\n * Drops the token held in memory.\n */\n clear(): Promise<void> {\n this.#token = null;\n\n return Promise.resolve();\n }\n}\n","/**\n * Base class for every error the SDK raises.\n *\n * Catching `AsterMDError` catches everything this SDK can throw and nothing it\n * cannot, which makes it the right thing to guard an integration boundary with.\n * Two branches descend from it: {@link TransportError} for failures where no HTTP\n * response was ever received, and {@link ApiError} for everything the server\n * answered with a non-2xx status.\n *\n * The class is abstract - the SDK never throws it directly.\n */\nexport abstract class AsterMDError extends Error {\n protected constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n // Restores the prototype chain so `instanceof` holds in downlevelled and\n // bundled output, where extending a built-in otherwise loses it.\n Object.setPrototypeOf(this, new.target.prototype);\n this.name = 'AsterMDError';\n\n if (typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(this, new.target);\n }\n }\n}\n","import { AsterMDError } from './astermd-error.js';\n\n/**\n * The API answered with a non-2xx status.\n *\n * This is the base for every status-carrying error and the class the SDK throws\n * for any status it does not map to something more specific - a 400, a 500, a\n * 502 from an upstream hiccup. The decoded response envelope is kept intact on\n * {@link ApiError.envelope} so an integrator can inspect whatever the server\n * chose to send alongside the status.\n *\n * Four subclasses cover the statuses worth branching on explicitly:\n * {@link AuthenticationError} (401), {@link NotFoundError} (404),\n * {@link ValidationError} (422), and {@link RateLimitError} (429).\n */\nexport class ApiError extends AsterMDError {\n readonly #statusCode: number;\n readonly #envelope: Readonly<Record<string, unknown>>;\n\n /**\n * @param message Human-readable message, taken from the envelope's `message`\n * field when the server sent one, otherwise `HTTP {status}`.\n * @param statusCode The HTTP status code the server returned.\n * @param envelope The decoded response body. Copied and frozen, so a later\n * mutation of the caller's object cannot change the error.\n */\n constructor(message: string, statusCode: number, envelope: Record<string, unknown>) {\n super(message);\n this.name = 'ApiError';\n this.#statusCode = statusCode;\n this.#envelope = Object.freeze({ ...envelope });\n }\n\n /**\n * Returns the HTTP status code that caused this error.\n *\n * @returns The status code, e.g. `404` or `500`.\n */\n statusCode(): number {\n return this.#statusCode;\n }\n\n /**\n * Returns the decoded response envelope as the server sent it.\n *\n * Useful for surfacing a server-supplied message or an error code the SDK does\n * not model. The object is frozen; treat it as read-only.\n *\n * @returns The decoded body, or an empty object when the response had none.\n */\n envelope(): Readonly<Record<string, unknown>> {\n return this.#envelope;\n }\n}\n","import { ApiError } from './api-error.js';\n\n/**\n * The API rejected the credentials or the bearer token (HTTP 401).\n *\n * The SDK does not raise this on the first 401. {@link TokenManager} refreshes\n * the token and the request is replayed once; only a second 401 surfaces here.\n * So seeing this error means a fresh token was also refused - check the client ID\n * and secret, and that the credentials are still active in your AsterMD\n * dashboard.\n */\nexport class AuthenticationError extends ApiError {\n constructor(message: string, statusCode: number, envelope: Record<string, unknown>) {\n super(message, statusCode, envelope);\n this.name = 'AuthenticationError';\n }\n}\n","import { ApiError } from './api-error.js';\n\n/**\n * The addressed resource does not exist (HTTP 404).\n *\n * Raised when an id in the request path matches nothing the calling organisation\n * can see. Note that the organisation scope lives inside the JWT, so an id\n * belonging to another organisation is indistinguishable from one that never\n * existed - both arrive here.\n */\nexport class NotFoundError extends ApiError {\n constructor(message: string, statusCode: number, envelope: Record<string, unknown>) {\n super(message, statusCode, envelope);\n this.name = 'NotFoundError';\n }\n}\n","import { ApiError } from './api-error.js';\n\n/**\n * The calling organisation exceeded its request allowance (HTTP 429).\n *\n * When the server sent a `Retry-After` header, its value in seconds is available\n * on {@link RateLimitError.retryAfter} - wait at least that long before trying\n * again. The header is optional, so treat `null` as \"back off on your own\n * schedule\" rather than \"retry immediately\".\n */\nexport class RateLimitError extends ApiError {\n readonly #retryAfter: number | null;\n\n /**\n * @param message Human-readable summary from the envelope.\n * @param statusCode Always 429 in practice.\n * @param envelope The decoded response body.\n * @param retryAfter Seconds to wait, from the `Retry-After` header, or `null`\n * when the header was absent or not a plain integer.\n */\n constructor(message: string, statusCode: number, envelope: Record<string, unknown>, retryAfter: number | null) {\n super(message, statusCode, envelope);\n this.name = 'RateLimitError';\n this.#retryAfter = retryAfter;\n }\n\n /**\n * Returns the server-advised wait in seconds before retrying.\n *\n * @returns Seconds to wait, or `null` when the server did not say.\n */\n retryAfter(): number | null {\n return this.#retryAfter;\n }\n}\n","import { AsterMDError } from './astermd-error.js';\n\n/**\n * A request never reached the API, or its response never came back.\n *\n * Raised for DNS failures, TLS errors, connection resets, and timeouts - every\n * case where the SDK has no HTTP status to reason about. The originating error is\n * preserved on `cause`. There is no `statusCode()` here by design: nothing was\n * returned to have a status.\n *\n * These are usually worth retrying with backoff; an {@link ApiError} usually is\n * not.\n */\nexport class TransportError extends AsterMDError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'TransportError';\n }\n}\n","import { ApiError } from './api-error.js';\n\n/**\n * The request was well-formed but the server refused its contents (HTTP 422).\n *\n * The interesting part is {@link ValidationError.fieldErrors}, which maps each\n * rejected field to the messages the server produced for it - exactly the shape\n * a form needs to render inline errors next to the offending inputs.\n */\nexport class ValidationError extends ApiError {\n readonly #fieldErrors: Readonly<Record<string, string[]>>;\n\n /**\n * @param message Human-readable summary from the envelope.\n * @param statusCode Always 422 in practice.\n * @param envelope The decoded response body.\n * @param fieldErrors Field name → list of messages, extracted from the\n * envelope's `errors` key by {@link Transport}.\n */\n constructor(\n message: string,\n statusCode: number,\n envelope: Record<string, unknown>,\n fieldErrors: Record<string, string[]>,\n ) {\n super(message, statusCode, envelope);\n this.name = 'ValidationError';\n this.#fieldErrors = Object.freeze({ ...fieldErrors });\n }\n\n /**\n * Returns the per-field validation messages the server produced.\n *\n * @returns Field name → messages. Empty when the server sent no `errors` key.\n */\n fieldErrors(): Readonly<Record<string, string[]>> {\n return this.#fieldErrors;\n }\n}\n","/** Options accepted by {@link Response}. */\nexport interface ResponseOptions<T> {\n /** HTTP status code of the response (200–299). */\n statusCode: number;\n /** Contents of the envelope's `data` key. */\n data: T;\n /** Contents of the envelope's `meta` key; may be empty. */\n meta: Record<string, unknown>;\n /** Human-readable `message` string from the envelope. */\n message: string;\n /** Raw response body, before decoding. */\n raw: string;\n}\n\n/**\n * Value object representing a successful API response with its envelope\n * already decoded.\n *\n * Every AsterMD response that returns HTTP 2xx is wrapped in a standard JSON\n * envelope:\n *\n * ```json\n * { \"success\": true, \"message\": \"Record created.\", \"data\": { }, \"meta\": { } }\n * ```\n *\n * {@link Transport} decodes that envelope and builds a `Response` from it.\n * `data()` holds the primary resource payload - `{ session: \"<uuid>\" }` for\n * session creation, for instance - while `meta()` carries pagination cursors and\n * other out-of-band information where the endpoint emits them.\n *\n * Non-2xx responses are never wrapped in this object; they raise\n * {@link ApiError} or one of its subclasses instead.\n *\n * The shape of `data()` varies by endpoint, so it defaults to an untyped record.\n * A caller who knows the shape may narrow it:\n *\n * ```ts\n * const patient = await client.patients().view<Patient>('p-1');\n * ```\n *\n * The instance and its top-level `data`/`meta` objects are frozen, which guards\n * against accidental reassignment or a stray `delete` on this response. That\n * freeze is shallow, though: an object or array nested inside `data()` is the\n * server's payload passed through unchanged, and remains as mutable as any\n * other object you receive from `JSON.parse`. Treat nested values as you would\n * any other deserialised response - copy before mutating if that matters to\n * you - rather than relying on this class to protect them.\n */\nexport class Response<T = Record<string, unknown>> {\n readonly #statusCode: number;\n readonly #data: T;\n readonly #meta: Readonly<Record<string, unknown>>;\n readonly #message: string;\n readonly #raw: string;\n\n /**\n * @param options The decoded envelope fields.\n */\n constructor(options: ResponseOptions<T>) {\n this.#statusCode = options.statusCode;\n this.#data = Object.freeze(Array.isArray(options.data) ? ([...options.data] as T) : { ...options.data });\n this.#meta = Object.freeze({ ...options.meta });\n this.#message = options.message;\n this.#raw = options.raw;\n\n Object.freeze(this);\n }\n\n /**\n * Returns the HTTP status code, always 200–299 for a `Response` instance.\n *\n * @returns The status code, e.g. `200` or `201`.\n */\n statusCode(): number {\n return this.#statusCode;\n }\n\n /**\n * Returns the primary resource payload from the envelope's `data` key.\n *\n * The shape varies by endpoint - see the TSDoc on the resource method you\n * called. Empty when the server returned no data.\n *\n * @returns The decoded `data` object.\n */\n data(): T {\n return this.#data;\n }\n\n /**\n * Returns the envelope's `meta` key, typically pagination information.\n *\n * Present on paginated list endpoints such as `products/list` and\n * `treatments/list`, where it may carry `total`, `per_page`, `current_page`,\n * and `last_page`. Empty for endpoints that emit no `meta` block.\n *\n * @returns The decoded `meta` object.\n */\n meta(): Readonly<Record<string, unknown>> {\n return this.#meta;\n }\n\n /**\n * Returns the human-readable `message` string from the envelope.\n *\n * Usually a short confirmation such as `\"Session created.\"`. Empty when the\n * server omitted the field.\n *\n * @returns The envelope message, possibly empty.\n */\n message(): string {\n return this.#message;\n }\n\n /**\n * Returns the raw response body exactly as received, before decoding.\n *\n * Useful for low-level debugging. Do not log this value for endpoints carrying\n * PHI, such as `patients/health-information`.\n *\n * @returns The raw JSON body.\n */\n raw(): string {\n return this.#raw;\n }\n}\n","import {\n ApiError,\n AsterMDError,\n AuthenticationError,\n NotFoundError,\n RateLimitError,\n TransportError,\n ValidationError,\n} from '../errors/index.js';\nimport { Response as ApiResponse } from '../response.js';\nimport type { FileUpload } from './file-upload.js';\nimport type { HttpClient } from './http-client.js';\nimport type { QueryParams, UrlBuilder } from './url-builder.js';\n\n/** One outbound API call, described declaratively. */\nexport interface SendOptions {\n /** Service segment of the URL: `sales`, `auth`, or `platform`. */\n service: string;\n /** HTTP method in uppercase. */\n method: string;\n /** Path relative to `/v1/{service}`, with `{placeholder}` tokens. */\n path: string;\n /** Placeholder name → value, substituted into `path`. */\n pathParams?: Record<string, string | number>;\n /** Query string parameters. */\n query?: QueryParams;\n /**\n * Request body, serialised as JSON. An empty object is sent as `{}`, which\n * every AsterMD endpoint requires of an object-shaped body. Omit for no body.\n * Mutually exclusive with `file`.\n */\n body?: Record<string, unknown> | null;\n /** Additional headers, e.g. `x-phi-verification-token`. */\n headers?: Record<string, string>;\n /**\n * File to send as a `multipart/form-data` body under the field name `file`.\n * Mutually exclusive with `body`.\n */\n file?: FileUpload | null;\n}\n\n/** Collaborators {@link Transport} needs. */\nexport interface TransportOptions {\n /** The client all outbound calls go through. */\n httpClient: HttpClient;\n /** Builds request URLs. */\n urlBuilder: UrlBuilder;\n /**\n * Returns the current bearer token, or `null` for an unauthenticated request.\n * {@link TokenManager} passes a provider that always returns `null` for its own\n * credential exchange, which is what stops that call recursing.\n */\n tokenProvider: () => Promise<string | null>;\n /**\n * Invoked at most once per `send()` when the server answers 401. Should\n * refresh the token and resolve `true` to request a replay, or `false` to let\n * the 401 surface.\n */\n onUnauthorized: () => Promise<boolean>;\n}\n\n/**\n * The single HTTP chokepoint for every outbound API call.\n *\n * Each resource method delegates to {@link Transport.send}, which owns the whole\n * request lifecycle: URL construction, bearer-token attachment, request\n * building, body serialisation, dispatch through the {@link HttpClient} seam,\n * envelope decoding, and the mapping from HTTP status to error class. Resources\n * never see a `Request`, a `Response`, or a line of JSON - that separation is\n * what keeps seventeen resource classes trivial and testable.\n *\n * The 401-retry protocol is built in. On a 401 the transport calls\n * `onUnauthorized` once, which in the production wiring refreshes the token, and\n * replays the request if that resolves `true`. A second 401 raises\n * {@link AuthenticationError} with no further retry.\n *\n * Status mapping:\n *\n * - 200–299 → {@link Response} with the envelope decoded\n * - 401 → {@link AuthenticationError}\n * - 404 → {@link NotFoundError}\n * - 422 → {@link ValidationError}, with field errors extracted from `errors`\n * - 429 → {@link RateLimitError}, with `Retry-After` parsed when present\n * - anything else → {@link ApiError}\n * - no response at all → {@link TransportError}, from the {@link HttpClient}\n */\nexport class Transport {\n readonly #httpClient: HttpClient;\n readonly #urlBuilder: UrlBuilder;\n readonly #tokenProvider: () => Promise<string | null>;\n readonly #onUnauthorized: () => Promise<boolean>;\n\n /**\n * @param options The collaborators to use for every call.\n */\n constructor(options: TransportOptions) {\n this.#httpClient = options.httpClient;\n this.#urlBuilder = options.urlBuilder;\n this.#tokenProvider = options.tokenProvider;\n this.#onUnauthorized = options.onUnauthorized;\n }\n\n /**\n * Dispatches one API call and returns the decoded success envelope.\n *\n * On a 401 the token is refreshed and the call is replayed once before\n * {@link AuthenticationError} is raised.\n *\n * @param options The call to make.\n * @returns The decoded envelope; `data()` holds the resource payload.\n * @throws {TypeError} If both `body` and `file` are supplied.\n * @throws {AuthenticationError} On a 401 that a token refresh did not recover.\n * @throws {NotFoundError} On a 404.\n * @throws {ValidationError} On a 422; inspect `fieldErrors()`.\n * @throws {RateLimitError} On a 429; inspect `retryAfter()`.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async send<T = Record<string, unknown>>(options: SendOptions): Promise<ApiResponse<T>> {\n if (options.body != null && options.file != null) {\n throw new TypeError('A request carries either a JSON body or a file upload, not both.');\n }\n\n let response = await this.#dispatch(options);\n\n if (response.status === 401 && (await this.#onUnauthorized())) {\n void response.body?.cancel();\n response = await this.#dispatch(options);\n }\n\n return await this.#handle<T>(response);\n }\n\n /**\n * Builds and sends one attempt.\n *\n * A fresh `Request` is constructed on every call rather than reused across the\n * 401 replay. A request body is a single-use stream: sending an already-sent\n * `Request` again transmits an empty body, silently. Keeping the description\n * and materialising the request per attempt is what makes the replay correct.\n */\n async #dispatch(options: SendOptions): Promise<Response> {\n const url = this.#urlBuilder.build(options.service, options.path, options.pathParams ?? {}, options.query ?? {});\n\n const headers = new Headers({ Accept: 'application/json' });\n const token = await this.#tokenProvider();\n\n if (token !== null) {\n headers.set('Authorization', `Bearer ${token}`);\n }\n\n for (const [name, value] of Object.entries(options.headers ?? {})) {\n headers.set(name, value);\n }\n\n let body: string | FormData | null = null;\n\n if (options.body != null) {\n headers.set('Content-Type', 'application/json');\n body = JSON.stringify(options.body);\n } else if (options.file != null) {\n // Every AsterMD endpoint that accepts bytes uses the field name `file`.\n // FormData sets the multipart content type and a fresh boundary itself.\n const form = new FormData();\n form.append(\n 'file',\n new Blob([options.file.contents()], { type: options.file.mimeType() }),\n options.file.fileName(),\n );\n body = form;\n }\n\n try {\n return await this.#httpClient.sendRequest(new Request(url, { method: options.method, headers, body }));\n } catch (error) {\n if (error instanceof AsterMDError) {\n throw error;\n }\n\n const detail = error instanceof Error ? error.message : String(error);\n\n throw new TransportError(`Request to ${url} failed: ${detail}`, {\n cause: error,\n });\n }\n }\n\n async #handle<T>(response: Response): Promise<ApiResponse<T>> {\n let raw: string;\n\n try {\n raw = await response.text();\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n\n throw new TransportError(`Reading the response body failed: ${detail}`, {\n cause: error,\n });\n }\n\n const envelope = decode(raw);\n const status = response.status;\n\n if (status >= 200 && status < 300) {\n return new ApiResponse<T>({\n statusCode: status,\n data: objectField(envelope, 'data') as T,\n meta: objectField(envelope, 'meta'),\n message: typeof envelope.message === 'string' ? envelope.message : '',\n raw,\n });\n }\n\n const message = typeof envelope.message === 'string' ? envelope.message : `HTTP ${status}`;\n\n switch (status) {\n case 401:\n throw new AuthenticationError(message, status, envelope);\n case 404:\n throw new NotFoundError(message, status, envelope);\n case 422:\n throw new ValidationError(message, status, envelope, fieldErrors(envelope));\n case 429:\n throw new RateLimitError(message, status, envelope, retryAfter(response));\n default:\n throw new ApiError(message, status, envelope);\n }\n }\n}\n\n/** Decodes a response body, treating anything unparseable as an empty envelope. */\nfunction decode(raw: string): Record<string, unknown> {\n if (raw === '') {\n return {};\n }\n\n try {\n const decoded: unknown = JSON.parse(raw);\n\n return typeof decoded === 'object' && decoded !== null ? (decoded as Record<string, unknown>) : {};\n } catch {\n return {};\n }\n}\n\n/**\n * Reads one envelope field that should hold structured data.\n *\n * A list payload is preserved as a list - several endpoints return `data` as an\n * array - and anything scalar or missing becomes an empty object.\n */\nfunction objectField(envelope: Record<string, unknown>, key: string): Record<string, unknown> {\n const value = envelope[key];\n\n return typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : {};\n}\n\n/** Normalises the envelope's `errors` key into field name → messages. */\nfunction fieldErrors(envelope: Record<string, unknown>): Record<string, string[]> {\n const errors = envelope.errors;\n\n // An array-shaped `errors` payload has only integer keys, which PHP skips.\n // Surfacing them as field names \"0\", \"1\" would be worse than reporting none.\n if (typeof errors !== 'object' || errors === null || Array.isArray(errors)) {\n return {};\n }\n\n const out: Record<string, string[]> = {};\n\n for (const [field, messages] of Object.entries(errors as Record<string, unknown>)) {\n if (Array.isArray(messages)) {\n out[field] = messages.filter((message): message is string => typeof message === 'string');\n } else if (typeof messages === 'string') {\n out[field] = [messages];\n } else if (typeof messages === 'object' && messages !== null) {\n // PHP's is_array also accepts a JSON object here (an associative array\n // on the PHP side); take its values the same way the array branch does.\n out[field] = Object.values(messages).filter((message): message is string => typeof message === 'string');\n }\n }\n\n return out;\n}\n\n/** Parses `Retry-After`, accepting only a plain integer count of seconds. */\nfunction retryAfter(response: Response): number | null {\n const header = response.headers.get('Retry-After');\n\n return header !== null && /^\\d+$/.test(header) ? Number(header) : null;\n}\n","/**\n * Immutable value object holding a JWT bearer token and its server-stated\n * expiry.\n *\n * {@link TokenManager} builds one after a successful credential exchange and\n * hands it to a {@link TokenStore}. Before each request the manager calls\n * {@link Token.isExpired} to decide whether the cached token can be reused or a\n * fresh one must be acquired.\n *\n * The organisation the token authorises is encoded inside the JWT itself, which\n * is why no SDK method takes an organisation argument.\n */\nexport class Token {\n readonly #value: string;\n readonly #expiresAtMs: number;\n\n /**\n * @param value The raw JWT string, to be sent as `Authorization: Bearer <value>`.\n * @param expiresAt Expiry as declared by the token response's\n * `access_token_expiry` field. Copied, so a later mutation of\n * the caller's `Date` cannot change the token.\n */\n constructor(value: string, expiresAt: Date) {\n this.#value = value;\n this.#expiresAtMs = expiresAt.getTime();\n\n Object.freeze(this);\n }\n\n /**\n * Returns the raw JWT string.\n *\n * @returns The JWT, ready for use as a bearer credential.\n */\n value(): string {\n return this.#value;\n }\n\n /**\n * Returns the server-stated expiry.\n *\n * A fresh `Date` each call, so callers cannot mutate the token's state. Note\n * that the SDK treats the token as expired before this moment - see\n * {@link Token.isExpired}.\n *\n * @returns The expiry as declared in the token response.\n */\n expiresAt(): Date {\n return new Date(this.#expiresAtMs);\n }\n\n /**\n * Reports whether this token should be considered expired and replaced.\n *\n * The pre-buffer avoids a race where the token is valid when checked but\n * expires in transit before the server processes the request. The default 30\n * seconds means the SDK only trusts a token with at least half a minute of\n * validity left. Pass a custom value only when you know something specific\n * about clock skew in your environment.\n *\n * @param preBufferSeconds Seconds to subtract from the stated expiry when\n * computing the effective cutoff.\n * @returns `true` when the token is at or past its effective expiry.\n */\n isExpired(preBufferSeconds = 30): boolean {\n return Date.now() >= this.#expiresAtMs - preBufferSeconds * 1000;\n }\n}\n","import type { Config } from '../config.js';\nimport { AuthenticationError } from '../errors/index.js';\nimport type { HttpClient } from '../http/http-client.js';\nimport { Transport } from '../http/transport.js';\nimport type { UrlBuilder } from '../http/url-builder.js';\nimport { InMemoryTokenStore } from './in-memory-token-store.js';\nimport { Token } from './token.js';\nimport type { TokenStore } from './token-store.js';\n\n/** Collaborators {@link TokenManager} needs. */\nexport interface TokenManagerOptions {\n /** Credentials and connection settings. */\n config: Config;\n /** Client used for the credential exchange. */\n httpClient: HttpClient;\n /** URL builder, shared with the main transport. */\n urlBuilder: UrlBuilder;\n /** Storage for the cached JWT. Defaults to {@link InMemoryTokenStore}. */\n store?: TokenStore;\n /**\n * Seconds before the stated expiry at which the token is treated as expired,\n * guarding against clock skew. Defaults to 30.\n */\n expiryPreBufferSeconds?: number;\n}\n\n/**\n * Owns the bearer-token lifecycle: lazy acquisition, caching, expiry, refresh.\n *\n * This is the only place in the SDK that exchanges credentials for a JWT. It\n * dispatches that exchange through its own {@link Transport}, built with a token\n * provider that always returns `null` and an unauthorized handler that always\n * returns `false`. That is not a detail - it is what makes recursion impossible.\n * A manager that used the main transport would ask it for a bearer token, which\n * would ask the manager, which would ask the transport, forever.\n *\n * On each {@link TokenManager.bearerToken} call the store is checked for a token\n * that is present and not within `expiryPreBufferSeconds` of expiry. Otherwise\n * `POST /v1/auth/api-credentials/token` is called and the result stored.\n *\n * Acquisition is de-duplicated: concurrent callers arriving while an exchange is\n * in flight all await that same exchange rather than starting their own. On a\n * long-running Node server the first burst of traffic after startup would\n * otherwise fire one credential exchange per concurrent request.\n *\n * The organisation is encoded in the JWT the server issues, which is why no SDK\n * method accepts an organisation argument.\n */\nexport class TokenManager {\n readonly #config: Config;\n readonly #store: TokenStore;\n readonly #expiryPreBufferSeconds: number;\n readonly #tokenTransport: Transport;\n\n #inFlight: Promise<Token> | null = null;\n\n /**\n * @param options The collaborators and tuning to use.\n */\n constructor(options: TokenManagerOptions) {\n this.#config = options.config;\n this.#store = options.store ?? new InMemoryTokenStore();\n this.#expiryPreBufferSeconds = options.expiryPreBufferSeconds ?? 30;\n\n // Token acquisition runs through a transport with no token provider and no\n // 401 retry. Recursion here would be a bug, so it is made unrepresentable.\n this.#tokenTransport = new Transport({\n httpClient: options.httpClient,\n urlBuilder: options.urlBuilder,\n tokenProvider: () => Promise.resolve(null),\n onUnauthorized: () => Promise.resolve(false),\n });\n }\n\n /**\n * Returns a valid bearer token, acquiring one if the cache cannot serve it.\n *\n * A cached, unexpired token is returned with no HTTP call. Otherwise the\n * credential exchange runs and its result is stored before being returned. Two\n * concurrent calls that both miss the cache share a single exchange.\n *\n * @returns The raw JWT, ready for `Authorization: Bearer <value>`.\n * @throws {AuthenticationError} If the credentials are refused, or if the token\n * response omits `access_token` or `access_token_expiry`.\n * @throws {TransportError} If the exchange never completed.\n */\n async bearerToken(): Promise<string> {\n const cached = await this.#store.get();\n\n if (cached !== null && !cached.isExpired(this.#expiryPreBufferSeconds)) {\n return cached.value();\n }\n\n return (await this.#acquire()).value();\n }\n\n /**\n * Discards the cached token, acquires a fresh one, and reports `true`.\n *\n * Always resolves `true` on success so it can be wired straight to\n * {@link Transport}'s `onUnauthorized` hook, where `true` means \"token\n * refreshed, replay the request once\". A second 401 on that replay raises\n * {@link AuthenticationError} without calling this again.\n *\n * @returns `true`.\n * @throws {AuthenticationError} If the fresh acquisition fails.\n * @throws {TransportError} If the exchange never completed.\n */\n async refresh(): Promise<boolean> {\n await this.#store.clear();\n await this.#acquire();\n\n return true;\n }\n\n /**\n * Runs the credential exchange, sharing one in-flight attempt across callers.\n *\n * The in-flight promise is cleared in a `finally`, so a failed exchange is\n * never cached - the next caller gets a fresh attempt rather than the stored\n * rejection.\n */\n async #acquire(): Promise<Token> {\n this.#inFlight ??= this.#exchange().finally(() => {\n this.#inFlight = null;\n });\n\n return await this.#inFlight;\n }\n\n async #exchange(): Promise<Token> {\n const response = await this.#tokenTransport.send({\n service: 'auth',\n method: 'POST',\n path: '/api-credentials/token',\n body: {\n client_id: this.#config.clientId(),\n client_secret: this.#config.clientSecret(),\n },\n });\n\n const data = response.data();\n const value = data.access_token;\n const expiry = data.access_token_expiry;\n\n if (typeof value !== 'string' || typeof expiry !== 'string') {\n throw new AuthenticationError(\n 'Token response missing access_token / access_token_expiry.',\n response.statusCode(),\n { data },\n );\n }\n\n const expiresAt = new Date(expiry);\n\n if (Number.isNaN(expiresAt.getTime())) {\n throw new AuthenticationError(\n `Token response carried an unparseable access_token_expiry: ${expiry}`,\n response.statusCode(),\n { data },\n );\n }\n\n const token = new Token(value, expiresAt);\n await this.#store.put(token);\n\n return token;\n }\n}\n","/** Base URL of the AsterMD asset CDN, including the trailing slash. */\nconst ASSET_BASE_URL = 'https://cdn.astermd.com/';\n\n/** Default bare API hostname. */\nconst DEFAULT_BASE_HOST = 'api.astermd.com';\n\n/** Options accepted by {@link Config}. */\nexport interface ConfigOptions {\n /** OAuth2 client ID issued by AsterMD. Must be non-empty. */\n clientId: string;\n /** OAuth2 client secret corresponding to `clientId`. Must be non-empty. */\n clientSecret: string;\n /** Bare API hostname without scheme or path. Defaults to `api.astermd.com`. */\n baseHost?: string;\n /** Per-request timeout in seconds. Must be >= 1. Defaults to 10. */\n timeoutSeconds?: number;\n}\n\n/**\n * Immutable value object holding SDK configuration.\n *\n * Stores the OAuth2 credentials and connection settings every outbound request\n * needs. It is constructed once inside {@link AsterMDClient} and never mutated.\n * Validation happens eagerly in the constructor, so a misconfigured client fails\n * at boot rather than at its first network call.\n *\n * `baseHost` is a bare hostname. The SDK always prepends `https://` and appends\n * `/v1/{service}/{path}` itself - never pass a full URL.\n */\nexport class Config {\n readonly #clientId: string;\n readonly #clientSecret: string;\n readonly #baseHost: string;\n readonly #timeoutSeconds: number;\n\n /**\n * @param options Credentials and connection settings.\n * @throws {TypeError} If `clientId` or `clientSecret` is empty, if `baseHost`\n * contains a scheme or a path, or if `timeoutSeconds` is below 1.\n */\n constructor(options: ConfigOptions) {\n const { clientId, clientSecret, baseHost = DEFAULT_BASE_HOST, timeoutSeconds = 10 } = options;\n\n if (clientId === '' || clientSecret === '') {\n throw new TypeError('clientId and clientSecret must be non-empty.');\n }\n\n if (baseHost.includes('://') || baseHost.includes('/')) {\n throw new TypeError(`baseHost must be a bare host (no scheme, no path). Got: ${baseHost}`);\n }\n\n if (timeoutSeconds < 1) {\n throw new TypeError('timeoutSeconds must be >= 1.');\n }\n\n this.#clientId = clientId;\n this.#clientSecret = clientSecret;\n this.#baseHost = baseHost;\n this.#timeoutSeconds = timeoutSeconds;\n\n Object.freeze(this);\n }\n\n /**\n * Returns the OAuth2 client ID used to acquire JWT bearer tokens.\n *\n * @returns The client ID, guaranteed non-empty.\n */\n clientId(): string {\n return this.#clientId;\n }\n\n /**\n * Returns the OAuth2 client secret used to acquire JWT bearer tokens.\n *\n * @returns The client secret, guaranteed non-empty.\n */\n clientSecret(): string {\n return this.#clientSecret;\n }\n\n /**\n * Returns the bare API hostname used to build request URLs.\n *\n * Never contains a scheme or a path. {@link UrlBuilder} prepends `https://`\n * and appends `/v1/{service}/{path}` at request time.\n *\n * @returns The hostname, e.g. `api.astermd.com`.\n */\n baseHost(): string {\n return this.#baseHost;\n }\n\n /**\n * Returns the per-request HTTP timeout in seconds.\n *\n * Applied by {@link FetchHttpClient} as an `AbortSignal` deadline covering the\n * whole request. A custom {@link HttpClient} may use it at its own discretion.\n *\n * @returns The timeout in seconds, always >= 1.\n */\n timeoutSeconds(): number {\n return this.#timeoutSeconds;\n }\n\n /**\n * Builds the fully-qualified CDN URL for a media asset path returned by the API.\n *\n * API responses carry relative asset paths such as\n * `{org}/{channel}/products/{file}.png`. Pass one here to resolve it.\n *\n * Treat the result as a *fetch* URL, not a *serve* URL: download the asset\n * once, store it on your own filesystem or CDN, and serve your copy to end\n * users. Hot-linking the AsterMD CDN from a storefront is not supported and is\n * subject to rate limiting.\n *\n * @param path Relative asset path as returned by the API; a leading slash is\n * optional and is trimmed.\n * @returns The fully-qualified URL.\n */\n assetUrl(path: string): string {\n return ASSET_BASE_URL + path.replace(/^\\/+/, '');\n }\n}\n","import { TransportError } from '../errors/index.js';\nimport type { HttpClient } from './http-client.js';\n\n/**\n * The default {@link HttpClient}: the platform `fetch` plus a timeout.\n *\n * This is what {@link AsterMDClient} uses unless you inject something else. It\n * adds exactly two things to `fetch`. First, a deadline: every request carries an\n * `AbortSignal.timeout()` built from the configured `timeoutSeconds`, covering\n * connection and response together. Second, uniform failures: a DNS error, a\n * connection reset, a TLS failure, or a timeout all surface as\n * {@link TransportError} with the original error preserved on `cause`, so\n * integrators can catch one type instead of guessing at platform error shapes.\n *\n * It holds no connection state, so one instance is safely shared across\n * concurrent requests.\n */\nexport class FetchHttpClient implements HttpClient {\n readonly #timeoutSeconds: number;\n readonly #fetchImpl: typeof fetch;\n\n /**\n * @param timeoutSeconds Per-request deadline in seconds. Must be >= 1.\n * @param fetchImpl The `fetch` implementation to call. Defaults to the global\n * one; override it to supply an instrumented or stubbed\n * `fetch` without implementing the whole interface.\n * @throws {TypeError} If `timeoutSeconds` is below 1.\n */\n constructor(timeoutSeconds = 10, fetchImpl: typeof fetch = globalThis.fetch) {\n if (timeoutSeconds < 1) {\n throw new TypeError('timeoutSeconds must be >= 1.');\n }\n\n this.#timeoutSeconds = timeoutSeconds;\n this.#fetchImpl = fetchImpl;\n }\n\n /**\n * Sends the request through `fetch` under the configured deadline.\n *\n * @param request The request to send.\n * @returns The response as received, whatever its status.\n * @throws {TransportError} If the request aborts on the deadline or fails at\n * the network layer.\n */\n async sendRequest(request: Request): Promise<Response> {\n try {\n return await this.#fetchImpl(request, {\n signal: AbortSignal.timeout(this.#timeoutSeconds * 1000),\n });\n } catch (error) {\n if (error instanceof Error && error.name === 'TimeoutError') {\n throw new TransportError(`Request to ${request.url} timed out after ${this.#timeoutSeconds}s.`, {\n cause: error,\n });\n }\n\n const detail = error instanceof Error ? error.message : String(error);\n\n throw new TransportError(`Request to ${request.url} failed: ${detail}`, { cause: error });\n }\n }\n}\n","/** Header names whose values are always replaced, compared case-insensitively. */\nconst SENSITIVE_HEADERS = new Set(['authorization', 'x-phi-verification-token']);\n\n/** JSON field names whose values are always replaced, compared case-insensitively. */\nconst SENSITIVE_FIELDS = ['client_secret', 'access_token', 'refresh_token'];\n\n/** Path fragments that mark an endpoint as carrying PII or PHI. */\nconst PHI_PATH_FRAGMENTS = ['/patients', '/extensions/identity-verify'];\n\n/** Path fragments that mark a request body as raw file bytes. */\nconst BINARY_UPLOAD_FRAGMENTS = ['/intake-submissions/upload-file/', '/upload-file-multipart/part/'];\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Strips credentials and protected health information out of debug log entries.\n *\n * Debug logging renders every outbound request as a copy-pasteable curl command\n * (see {@link CurlFormatter}). Left untouched, those entries would contain the\n * bearer JWT, the client secret exchanged for it, and - on patient endpoints -\n * PHI. This class is the single place that decides what must never reach a log\n * sink, and {@link CurlFormatter} and {@link LoggingHttpClient} both apply it\n * whenever redaction is enabled.\n *\n * Redaction is **on by default**: constructing {@link AsterMDClient} with\n * `debug: true` produces safe logs. Pass `debugRedact: false` to get verbatim\n * output when you genuinely need to replay a request by hand - never in\n * production.\n *\n * Values replaced with {@link LogRedactor.PLACEHOLDER}:\n *\n * - the `Authorization` header value (the bearer JWT), scheme preserved;\n * - the `x-phi-verification-token` header value;\n * - the `client_secret` field of a token-exchange request body;\n * - the `access_token` and `refresh_token` fields of any response body.\n *\n * Bodies are dropped whole for `/patients/*` and `/extensions/identity-verify`,\n * which carry PII and PHI - a Social Security Number in the identity case - that\n * must not appear in a log under any circumstances.\n *\n * File-upload bodies are dropped too, for two reasons: the bytes of a scanned ID\n * or a consultation video are PHI, and rendering megabytes of binary into a curl\n * command would make the log unusable anyway. That covers the single-shot upload\n * endpoint and the multipart `part` endpoint. The multipart `initiate`, `finish`,\n * and `abort` bodies are small JSON control messages carrying no file contents,\n * so they are logged in full where they are useful.\n *\n * Redaction covers headers and bodies only - **the request URL is logged\n * verbatim**. Endpoints that take their input as a query parameter therefore log\n * that input, so entries for `extensions/email-verify`,\n * `extensions/geo-info`, `extensions/geo-blocklist`, and the address endpoints\n * contain the email address or IP address that was checked. Keep that in mind\n * when deciding where debug logs are written and how long they are kept.\n */\nexport class LogRedactor {\n /** Replacement text substituted for every redacted value. */\n static readonly PLACEHOLDER = '[REDACTED]';\n\n /** Marker written in place of a body dropped wholesale. */\n static readonly PHI_PLACEHOLDER = '[REDACTED - PHI endpoint]';\n\n /** Marker written in place of the raw file bytes of an upload. */\n static readonly UPLOAD_PLACEHOLDER = '[REDACTED - binary upload]';\n\n /**\n * Returns the loggable form of one request header value.\n *\n * A sensitive header keeps its name, so the log still shows the header was\n * sent, but its value is replaced. For `Authorization` the scheme survives -\n * `Bearer [REDACTED]` - because knowing the scheme helps when debugging and\n * carries no secret.\n *\n * @param name The header name as it appears on the request.\n * @param value The raw header value.\n * @returns The value to write to the log.\n */\n headerValue(name: string, value: string): string {\n if (!SENSITIVE_HEADERS.has(name.toLowerCase())) {\n return value;\n }\n\n const scheme = /^(\\S+)\\s+\\S/.exec(value);\n\n return scheme === null ? LogRedactor.PLACEHOLDER : `${scheme[1]!} ${LogRedactor.PLACEHOLDER}`;\n }\n\n /**\n * Returns the loggable form of a request or response body.\n *\n * A body belonging to a PHI-carrying path, and the raw bytes sent to an upload\n * endpoint, are replaced entirely. Every other body has its sensitive JSON\n * fields rewritten in place, leaving the surrounding structure intact so the\n * entry stays readable and replayable apart from the redacted values.\n *\n * @param body The raw body as it would otherwise be logged.\n * @param path The request path, used to detect PHI and upload endpoints.\n * @returns The body to write to the log.\n */\n body(body: string, path: string): string {\n if (body === '') {\n return body;\n }\n\n if (this.isPhiPath(path)) {\n return LogRedactor.PHI_PLACEHOLDER;\n }\n\n if (this.isBinaryUploadPath(path)) {\n return LogRedactor.UPLOAD_PLACEHOLDER;\n }\n\n return this.#redactFields(body);\n }\n\n /**\n * Reports whether a path carries PII or PHI and must have its body dropped.\n *\n * @param path The request path, with or without the `/v1/{service}` prefix.\n * @returns `true` for a patient endpoint or the identity-verification endpoint.\n */\n isPhiPath(path: string): boolean {\n const lower = path.toLowerCase();\n\n return PHI_PATH_FRAGMENTS.some(fragment => lower.includes(fragment));\n }\n\n /**\n * Reports whether a path carries raw file bytes and must have its body dropped.\n *\n * True for the single-shot intake-submission upload endpoint and the multipart\n * `part` endpoint. The multipart `initiate`, `finish`, and `abort` endpoints\n * carry small JSON control messages, so they are excluded and their bodies stay\n * in the log where they help.\n *\n * @param path The request path, with or without the `/v1/{service}` prefix.\n * @returns `true` when the body is file contents rather than JSON.\n */\n isBinaryUploadPath(path: string): boolean {\n const lower = path.toLowerCase();\n\n return BINARY_UPLOAD_FRAGMENTS.some(fragment => lower.includes(fragment));\n }\n\n /**\n * Extracts the path from a request, for use with the methods above.\n *\n * @param request The request to inspect.\n * @returns The URL path, without the query string.\n */\n pathOf(request: Request): string {\n return new URL(request.url).pathname;\n }\n\n /**\n * Replaces the value of every sensitive JSON field found in the given text.\n *\n * Operates on the serialised form rather than decoding, so it works for\n * pretty-printed and compact JSON alike and never reorders or reformats a body\n * that is only partly JSON.\n */\n #redactFields(body: string): string {\n let out = body;\n\n for (const field of SENSITIVE_FIELDS) {\n const pattern = new RegExp(`(\"${escapeRegExp(field)}\"\\\\s*:\\\\s*)\"(?:[^\"\\\\\\\\]|\\\\\\\\.)*\"`, 'gi');\n out = out.replace(pattern, `$1\"${LogRedactor.PLACEHOLDER}\"`);\n }\n\n return out;\n }\n}\n","import type { LogRedactor } from './log-redactor.js';\n\n/**\n * Converts a request into a copy-pasteable multi-line curl command.\n *\n * Used only by {@link LoggingHttpClient}, to produce log entries a developer can\n * replay in a terminal or import into an API client. The class has no instance\n * state and exposes a single static method.\n *\n * With a {@link LogRedactor} the rendered command has its credentials and PHI\n * stripped, so the entry stays structurally faithful while being safe to write to\n * disk or ship to an aggregator. Without one it is rendered verbatim and will\n * contain the live bearer token.\n *\n * Two details differ from the PHP formatter, both forced by the platform. The\n * method is asynchronous, because reading a request body is. And header names\n * appear lowercased, because `Headers` normalises them - curl does not care, and\n * the redactor compares names case-insensitively.\n */\nexport class CurlFormatter {\n /**\n * Renders the given request as a multi-line curl invocation.\n *\n * Each header becomes its own `--header '...'` line. A body is appended as\n * `--data '...'` with single quotes shell-escaped through the `'\\''` idiom.\n * With no body, the trailing backslash continuation is stripped from the last\n * line so the command pastes cleanly.\n *\n * The request's body is read from a clone, so the request itself stays unread\n * and can still be sent. Reading the original would leave the real request with\n * an empty body.\n *\n * @param request The request to format.\n * @param redactor Applied to header values and the body before rendering. Pass\n * `null` or omit it to render verbatim.\n * @returns The multi-line curl command.\n */\n static async format(request: Request, redactor: LogRedactor | null = null): Promise<string> {\n const lines = [`curl --location --request ${request.method} '${request.url}' \\\\`];\n const headerLines: string[] = [];\n\n for (const [name, value] of request.headers.entries()) {\n const shown = redactor?.headerValue(name, value) ?? value;\n headerLines.push(` --header '${name}: ${shown}' \\\\`);\n }\n\n let body = await readBody(request);\n\n if (redactor !== null) {\n body = redactor.body(body, redactor.pathOf(request));\n }\n\n if (body !== '') {\n lines.push(...headerLines);\n // Escape single quotes for the shell via the '\\'' idiom.\n lines.push(` --data '${body.replaceAll(\"'\", String.raw`'\\''`)}'`);\n } else {\n headerLines.forEach((line, index) => {\n lines.push(index === headerLines.length - 1 ? line.replace(/ \\\\$/, '') : line);\n });\n\n if (headerLines.length === 0) {\n lines[0] = lines[0]!.replace(/ \\\\$/, '');\n }\n }\n\n return lines.join('\\n');\n }\n}\n\n/**\n * Reads a request body without consuming the request.\n *\n * A multipart body is reported as its placeholder length rather than decoded: the\n * redactor drops upload bodies by path anyway, and materialising the bytes only to\n * throw them away wastes memory on exactly the requests that are largest.\n */\nasync function readBody(request: Request): Promise<string> {\n if (request.body === null) {\n return '';\n }\n\n const contentType = request.headers.get('content-type') ?? '';\n\n if (contentType.startsWith('multipart/form-data')) {\n return '[multipart/form-data]';\n }\n\n return await request.clone().text();\n}\n","import { CurlFormatter } from './curl-formatter.js';\nimport type { HttpClient } from './http-client.js';\nimport { LogRedactor } from './log-redactor.js';\n\n/**\n * Destination for debug log entries.\n *\n * Called once per request with a fully formatted entry - timestamp, curl\n * command, and response. Return a promise to have the SDK await the write. Any\n * error it throws is swallowed: a failing log must never fail an API call.\n */\nexport type DebugSink = (entry: string) => void | Promise<void>;\n\n/** Collaborators {@link LoggingHttpClient} needs. */\nexport interface LoggingHttpClientOptions {\n /** The client to delegate the actual request to. */\n inner: HttpClient;\n /** Where formatted entries are written. */\n sink: DebugSink;\n /** IANA time zone for entry timestamps. Defaults to `UTC`. */\n timeZone?: string;\n /**\n * Strips credentials and PHI from each entry. Defaults to a\n * {@link LogRedactor}; pass `null` to log verbatim, including live bearer\n * tokens - never in production.\n */\n redactor?: LogRedactor | null;\n}\n\n/**\n * {@link HttpClient} decorator that logs every request and its response as a\n * curl command.\n *\n * Wraps any client, forwards each call to it unchanged, and captures the request\n * and response for debug logging. Each entry carries a timestamp, the request\n * rendered as a replayable curl command by {@link CurlFormatter}, and the HTTP\n * status plus response body.\n *\n * {@link AsterMDClient} applies this decorator *before* handing the client to\n * {@link TokenManager}, so one decorator covers the credential exchange as well\n * as every resource call. That also makes it the only place in the SDK that could\n * leak a credential, which is why a {@link LogRedactor} is attached by default.\n *\n * The response body is read from a clone and the caller receives a fresh response\n * built from the bytes already read, so `Transport` can still decode the\n * envelope. A sink that throws is ignored, and an asynchronous sink is awaited\n * before the response is returned.\n */\nexport class LoggingHttpClient implements HttpClient {\n readonly #inner: HttpClient;\n readonly #sink: DebugSink;\n readonly #redactor: LogRedactor | null;\n readonly #formatter: Intl.DateTimeFormat;\n\n /**\n * @param options The client to wrap and where to log.\n */\n constructor(options: LoggingHttpClientOptions) {\n this.#inner = options.inner;\n this.#sink = options.sink;\n this.#redactor = options.redactor === undefined ? new LogRedactor() : options.redactor;\n this.#formatter = new Intl.DateTimeFormat('en-CA', {\n timeZone: options.timeZone ?? 'UTC',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n hourCycle: 'h23',\n timeZoneName: 'short',\n });\n }\n\n /**\n * Forwards the request to the inner client, then logs the exchange.\n *\n * @param request The request to send and log.\n * @returns The response, with its body still readable.\n * @throws The inner client's error, rethrown after being logged.\n */\n async sendRequest(request: Request): Promise<Response> {\n const timestamp = this.#timestamp();\n const curl = await CurlFormatter.format(request, this.#redactor);\n\n let response: Response;\n\n try {\n response = await this.#inner.sendRequest(request);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n await this.#write(`[${timestamp}]\\n${curl}\\n\\n# Transport error: ${detail}\\n\\n`);\n\n throw error;\n }\n\n // Read the body once, then hand the caller a response rebuilt from those\n // bytes. A response body is a single-use stream, so logging it would\n // otherwise leave Transport with nothing to decode.\n const raw = await response.clone().text();\n const shown = this.#redactor?.body(raw, this.#redactor.pathOf(request)) ?? raw;\n\n await this.#write(`[${timestamp}]\\n${curl}\\n\\n# Response: HTTP ${response.status}\\n${shown}\\n\\n`);\n\n // 204/205/304 must be constructed with a null body; an empty string is\n // still a body and the Response constructor rejects it.\n const nullBody = response.status === 204 || response.status === 205 || response.status === 304;\n\n return new Response(nullBody ? null : raw, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n }\n\n async #write(entry: string): Promise<void> {\n try {\n await this.#sink(entry);\n } catch {\n // Losing a debug entry is never worth failing the request that produced it.\n }\n }\n\n /**\n * Formats the current instant as `YYYY-MM-DD HH:MM:SS.ffffff ZONE`.\n *\n * PHP logs microseconds; `Date` only resolves to milliseconds, so the\n * millisecond value is zero-padded to six digits to keep the column width and\n * the shape of the PHP entries.\n */\n #timestamp(): string {\n const now = new Date();\n const parts = new Map(this.#formatter.formatToParts(now).map(({ type, value }) => [type, value]));\n const micros = String(now.getMilliseconds()).padStart(3, '0').padEnd(6, '0');\n\n return (\n `${parts.get('year')!}-${parts.get('month')!}-${parts.get('day')!} ` +\n `${parts.get('hour')!}:${parts.get('minute')!}:${parts.get('second')!}.${micros} ` +\n `${parts.get('timeZoneName')!}`\n );\n }\n}\n","/** Query string parameters accepted by {@link UrlBuilder.build}. */\nexport type QueryParams = Record<string, string | number | boolean>;\n\n/**\n * Percent-encodes a value the way PHP's `rawurlencode()` does.\n *\n * `encodeURIComponent()` leaves `!`, `'`, `(`, `)`, and `*` unescaped, which\n * `rawurlencode()` escapes. The difference matters because URLs built by the PHP\n * SDK and by this one are compared against the same server and against each\n * other in tests.\n */\nfunction rawUrlEncode(value: string): string {\n return encodeURIComponent(value).replace(\n /[!'()*]/g,\n character => `%${character.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n}\n\n/**\n * Serialises query parameters the way PHP's `http_build_query()` does.\n *\n * That means RFC 1738 encoding: a space becomes `+`, and `~` becomes `%7E`.\n * `URLSearchParams` agrees on the space but not on the tilde, so this is written\n * out rather than delegated. Booleans serialise as `1` and `0`, matching PHP's\n * coercion of `true` and `false`.\n */\nfunction buildQuery(query: QueryParams): string {\n return Object.entries(query)\n .map(([key, value]) => {\n const raw = typeof value === 'boolean' ? (value ? '1' : '0') : String(value);\n\n return `${urlEncode(key)}=${urlEncode(raw)}`;\n })\n .join('&');\n}\n\nfunction urlEncode(value: string): string {\n return rawUrlEncode(value).replace(/%20/g, '+').replace(/~/g, '%7E');\n}\n\n/**\n * Builds fully-qualified HTTPS request URLs following the `/v1/{service}/{path}`\n * convention.\n *\n * Every AsterMD endpoint lives under `https://{host}/v1/{service}/{path}`, where\n * `{service}` is `auth`, `sales`, or `platform`. This class is the one place that\n * convention is encoded, so resources never construct URLs and the SDK can never\n * be handed a full URL by a consumer.\n *\n * Path placeholders such as `{id}` are substituted and percent-encoded; query\n * parameters are appended with PHP-compatible encoding so a URL built here is\n * byte-identical to the one the PHP SDK would build for the same call.\n */\nexport class UrlBuilder {\n readonly #baseHost: string;\n\n /**\n * @param baseHost Bare hostname to prepend, e.g. `api.astermd.com`.\n */\n constructor(baseHost: string) {\n this.#baseHost = baseHost;\n }\n\n /**\n * Constructs the fully-qualified URL for a service and path.\n *\n * Placeholder tokens in `path` - `{id}`, `{session}`, `{form_json_identifier}`\n * - are replaced with the matching values from `pathParams`, percent-encoded.\n * A placeholder with no matching entry is left untouched rather than guessed\n * at, which surfaces the programming error in the request URL. Non-empty\n * `query` entries are serialised and appended after `?`.\n *\n * @param service Service segment, e.g. `sales`, `auth`, `platform`.\n * @param path Path relative to `/v1/{service}`, with optional `{placeholder}`\n * tokens.\n * @param pathParams Placeholder name → replacement value.\n * @param query Query string parameters.\n * @returns The fully-qualified URL.\n */\n build(\n service: string,\n path: string,\n pathParams: Record<string, string | number> = {},\n query: QueryParams = {},\n ): string {\n let resolved = path;\n\n for (const [key, value] of Object.entries(pathParams)) {\n resolved = resolved.split(`{${key}}`).join(rawUrlEncode(String(value)));\n }\n\n const url = `https://${this.#baseHost}/v1/${service}${resolved}`;\n const queryString = Object.keys(query).length > 0 ? buildQuery(query) : '';\n\n return queryString === '' ? url : `${url}?${queryString}`;\n }\n}\n","import { appendFile, mkdir, readdir, rm } from 'node:fs/promises';\nimport { basename, dirname, extname, join } from 'node:path';\n\n/** Options accepted by {@link DailyFileLogSink}. */\nexport interface DailyFileLogSinkOptions {\n /**\n * Undated path the daily filenames are derived from, e.g.\n * `/var/log/astermd/sdk.log`. A path with no extension gets `.log`.\n */\n basePath: string;\n /**\n * Days of logs to keep. A file is removed once its date is more than this many\n * days before today. Use `0` to keep every file forever. Defaults to 7.\n */\n retentionDays?: number;\n /**\n * IANA time zone deciding which calendar day an entry belongs to. Pass the same\n * zone used for log timestamps so filenames and their contents agree. Defaults\n * to `UTC`.\n */\n timeZone?: string;\n}\n\n/**\n * Debug-log sink that writes one file per calendar day and prunes old files.\n *\n * The debug logger emits one entry per HTTP request. Appending those to a single\n * file grows without bound, which is why this sink dates each file and deletes\n * the ones that have aged out - the same arrangement as conventional daily log\n * rotation, but self-contained, so no external rotation tooling is needed.\n *\n * You give it a *base path* and it derives dated filenames from it, keeping the\n * directory, stem, and extension you chose:\n *\n * ```\n * basePath: /var/log/astermd/sdk.log\n *\n * /var/log/astermd/sdk-2026-09-08.log ← today, appended to\n * /var/log/astermd/sdk-2026-09-07.log\n * /var/log/astermd/sdk-2026-09-01.log ← deleted once it is over 7 days old\n * ```\n *\n * {@link AsterMDClient} builds one for you when you pass `debugFile`; construct\n * it directly only if you want to reuse one sink across several clients.\n *\n * Pruning reads the date out of each filename rather than the filesystem's\n * modification time, so a file that was merely touched is still removed on\n * schedule and unrelated files sharing the directory are never matched. It runs\n * at most once per instance, on the first write, which keeps it off the hot path.\n *\n * Writes are serialized through an internal promise chain, so concurrent requests\n * cannot interleave halves of two entries. Every I/O failure is swallowed: losing\n * a debug entry is never worth failing the request that produced it.\n *\n * Supplying your own `debugSink` bypasses this class entirely, and retention then\n * becomes that system's responsibility.\n */\nexport class DailyFileLogSink {\n readonly #directory: string;\n readonly #stem: string;\n readonly #extension: string;\n readonly #retentionDays: number;\n readonly #dateFormatter: Intl.DateTimeFormat;\n\n #pruned = false;\n #tail: Promise<void> = Promise.resolve();\n\n /**\n * @param options Base path, retention window, and time zone.\n * @throws {TypeError} If `basePath` is empty or carries no file name, or if\n * `retentionDays` is negative.\n */\n constructor(options: DailyFileLogSinkOptions) {\n const { basePath, retentionDays = 7, timeZone = 'UTC' } = options;\n\n if (basePath.trim() === '') {\n throw new TypeError('basePath must be a non-empty file path.');\n }\n\n if (retentionDays < 0) {\n throw new TypeError('retentionDays must be >= 0 (0 disables pruning).');\n }\n\n const extension = extname(basePath);\n const stem = basename(basePath, extension);\n\n if (stem === '') {\n throw new TypeError(`basePath must include a file name. Got: ${basePath}`);\n }\n\n this.#directory = dirname(basePath);\n this.#stem = stem;\n this.#extension = extension === '' ? '.log' : extension;\n this.#retentionDays = retentionDays;\n this.#dateFormatter = new Intl.DateTimeFormat('en-CA', {\n timeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n });\n }\n\n /**\n * Appends one entry to today's log file, pruning aged-out files on the first\n * call.\n *\n * Resolves once the entry is on disk. Writes are chained, so entries land whole\n * and in call order even under concurrency. Never rejects: an I/O failure is\n * swallowed rather than propagated into the API call being logged.\n *\n * @param entry The formatted log entry to append.\n */\n async write(entry: string): Promise<void> {\n this.#tail = this.#tail.then(() => this.#append(entry)).catch(() => undefined);\n\n await this.#tail;\n }\n\n /**\n * Returns the absolute path entries are currently written to.\n *\n * Useful in tests and for a \"where are my logs\" diagnostic. The path changes at\n * midnight in the configured time zone.\n *\n * @returns The dated file path, e.g. `/var/log/astermd/sdk-2026-09-08.log`.\n */\n currentFile(): string {\n return join(this.#directory, `${this.#stem}-${this.#today()}${this.#extension}`);\n }\n\n async #append(entry: string): Promise<void> {\n try {\n // Owner-only: with debugRedact: false a raw entry can carry a live\n // bearer token, so the directory and file must not be readable by every\n // user on the host, the same rule FileTokenStore already follows.\n await mkdir(this.#directory, { recursive: true, mode: 0o700 });\n await appendFile(this.currentFile(), entry, { mode: 0o600 });\n\n if (!this.#pruned) {\n this.#pruned = true;\n await this.#prune();\n }\n } catch {\n // Losing a debug entry is never worth failing the request that produced it.\n }\n }\n\n /**\n * Deletes dated log files older than the retention window.\n *\n * Only files matching this sink's own `{stem}-YYYY-MM-DD{ext}` pattern are\n * considered, and the date comes from the filename rather than filesystem\n * metadata. A file that cannot be deleted is skipped silently.\n */\n async #prune(): Promise<void> {\n if (this.#retentionDays === 0) {\n return;\n }\n\n const cutoff = this.#cutoff();\n const pattern = new RegExp(`^${escapeRegExp(this.#stem)}-(\\\\d{4}-\\\\d{2}-\\\\d{2})${escapeRegExp(this.#extension)}$`);\n\n let names: string[];\n\n try {\n names = await readdir(this.#directory);\n } catch {\n return;\n }\n\n for (const name of names) {\n const matched = pattern.exec(name);\n\n // ISO dates sort lexicographically, so a string comparison is a date\n // comparison. A file exactly `retentionDays` old is kept, matching PHP.\n if (matched !== null && matched[1]! < cutoff) {\n try {\n await rm(join(this.#directory, name), { force: true });\n } catch {\n // A file we cannot delete is not worth failing over; PHP's @unlink\n // skips silently too. Try the next one.\n }\n }\n }\n }\n\n #today(): string {\n return this.#dateFormatter.format(new Date());\n }\n\n /** The oldest date still kept, as `YYYY-MM-DD`. */\n #cutoff(): string {\n const today = new Date(`${this.#today()}T00:00:00.000Z`);\n today.setUTCDate(today.getUTCDate() - this.#retentionDays);\n\n return today.toISOString().slice(0, 10);\n }\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n","import type { Transport } from '../http/transport.js';\n\n/**\n * Base class for every SDK resource; holds the shared {@link Transport}.\n *\n * Resource classes extend this and call `this.transport.send()` to dispatch every\n * outbound request. A resource never builds a request, touches a header, or\n * decodes JSON - all of that belongs to `Transport` alone. That is what keeps\n * seventeen resource classes small enough to read at a glance and testable\n * without a network.\n */\nexport abstract class AbstractResource {\n /** The shared transport used for all outbound API calls. */\n protected readonly transport: Transport;\n\n /**\n * @param transport The transport every call from this resource goes through.\n */\n protected constructor(transport: Transport) {\n this.transport = transport;\n }\n}\n","import type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/** Options for {@link Carts.create} and {@link Carts.update}. */\nexport interface CartWriteOptions {\n /** The session the cart belongs to. */\n session: string;\n /**\n * The cart's full contents. Each entry identifies a product and quantity, and\n * a variant where the product has them. This is the complete list, not a\n * delta - see the API reference in your AsterMD dashboard for the fields.\n */\n items: Record<string, unknown>[];\n}\n\n/**\n * Carts - what the visitor intends to buy, recorded against their session.\n *\n * The cart is a server-side record of intent, kept so abandonment can be measured\n * and so the eventual order can be reconciled against what was selected. It is\n * not a pricing engine and not a checkout: money is handled by your own payment\n * flow, and the resulting order arrives through\n * {@link Treatments.create} or {@link Treatments.sync}.\n *\n * Both methods take the cart's *whole* contents rather than a delta. Mirror your\n * storefront's cart state into `update()` whenever it changes, and the\n * server-side record stays in step with what the visitor is actually looking at.\n */\nexport class Carts extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Creates the cart for a session.\n *\n * Call this the first time the visitor puts something in their cart. The\n * session must already exist. If a cart is already recorded for the session,\n * use {@link Carts.update} instead.\n *\n * @param options The session and the cart's contents.\n * @returns The created cart.\n * @throws {ValidationError} If the session or an item is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async create<T = Record<string, unknown>>(options: CartWriteOptions): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/carts/create',\n body: { session: options.session, items: options.items },\n });\n }\n\n /**\n * Replaces the contents of a session's cart.\n *\n * The item list is authoritative: whatever you send becomes the cart. Send the\n * full list on every change rather than only what moved, and pass an empty list\n * to record that the visitor emptied their cart.\n *\n * @param options The session and the cart's new contents.\n * @returns The updated cart.\n * @throws {NotFoundError} If no cart exists for the session.\n * @throws {ValidationError} If an item is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async update<T = Record<string, unknown>>(options: CartWriteOptions): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PUT',\n path: '/carts/update/{session}',\n pathParams: { session: options.session },\n body: { items: options.items },\n });\n }\n}\n","import type { QueryParams } from '../http/url-builder.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Categories - how the catalogue is grouped for browsing.\n *\n * A category is a merchandising grouping a storefront renders as navigation: a\n * condition treated, a product family, a landing-page collection. Products\n * reference the categories they belong to, so fetching the category list is\n * usually the first call a catalogue page makes.\n *\n * Read-only, and configured in your AsterMD dashboard.\n */\nexport class Categories extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Lists categories, filtered and paginated.\n *\n * Pagination details come back in `meta()`. The accepted filters are in the\n * API reference in your AsterMD dashboard.\n *\n * @param query Filters and pagination parameters.\n * @returns The matching categories, with pagination in `meta()`.\n * @throws {ValidationError} If a filter is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async list<T = Record<string, unknown>>(query: QueryParams = {}): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/categories/list',\n query,\n });\n }\n\n /**\n * Fetches one category.\n *\n * @param id The category's id.\n * @returns The category record.\n * @throws {NotFoundError} If the category does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/categories/view/{id}',\n pathParams: { id },\n });\n }\n}\n","import type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Channels - the storefront's own configuration, as the server holds it.\n *\n * A channel is one selling surface: a brand, a site, a campaign landing page.\n * Which products it may sell, which payment processor it uses, and which\n * questionnaires those products require are all channel-level configuration set\n * in your AsterMD dashboard. Your storefront reads that configuration rather than\n * duplicating it, so changing what a brand sells does not need a deployment.\n *\n * Three views, in increasing depth. {@link Channels.view} returns the channel\n * itself. {@link Channels.assignedProducts} returns what it may sell.\n * {@link Channels.details} returns the whole tree - channel, payment processor,\n * products, variants, prices, and per-integration mappings - in a single call,\n * which is what a storefront usually wants at boot.\n *\n * Channel configuration changes rarely. Fetch it rather than embedding a copy,\n * but caching the result for minutes is reasonable.\n */\nexport class Channels extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Fetches a channel's own configuration.\n *\n * The channel record itself - name, type, status - without its product\n * catalogue.\n *\n * @param id The channel's id.\n * @returns The channel record.\n * @throws {NotFoundError} If the channel does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/channels/view/{id}',\n pathParams: { id },\n });\n }\n\n /**\n * Fetches the products assigned to a channel.\n *\n * What this selling surface is permitted to sell. Use it to build a catalogue\n * page without pulling the full detail tree.\n *\n * @param id The channel's id.\n * @returns The assigned products.\n * @throws {NotFoundError} If the channel does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async assignedProducts<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/channels/assigned-products/{id}',\n pathParams: { id },\n });\n }\n\n /**\n * Fetches a channel's complete configuration tree.\n *\n * Channel, payment processor, products, variants, prices, and per-integration\n * mappings in one response - everything a storefront needs to render its\n * catalogue without further calls. The envelope is deeply nested and carries\n * server-side field names; see the API reference in your AsterMD dashboard for\n * its shape. Pass `data()` through {@link ChannelDetail.from} to flatten it.\n *\n * @param id The channel's id.\n * @returns The full configuration tree.\n * @throws {NotFoundError} If the channel does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async details<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/channels/detail/{id}',\n pathParams: { id },\n });\n }\n}\n","/**\n * Checkout-funnel events recorded against a session.\n *\n * {@link CheckoutEvents.create} always records `CheckoutVisited` - it is the\n * call that opens the funnel record. Every later state is reported through\n * {@link CheckoutEvents.update} with one of the remaining values, which lets the\n * server reconstruct where a prospect abandoned checkout and which upsells they\n * were shown.\n */\nexport const CheckoutEvent = Object.freeze({\n /** The prospect reached the checkout page. Recorded implicitly by `create()`. */\n CheckoutVisited: 'checkout_visited',\n /** An upsell was presented to the prospect. */\n UpsellOffered: 'upsell_offered',\n /** The prospect accepted a presented upsell. */\n UpsellAccepted: 'upsell_accepted',\n /** The prospect declined a presented upsell. */\n UpsellDeclined: 'upsell_declined',\n /** The order was placed successfully. */\n OrderPlaced: 'order_placed',\n /** The order was declined, typically by the payment processor. */\n OrderDeclined: 'order_declined',\n} as const);\n\n/** Any checkout-funnel event wire value. A bare string literal satisfies this type. */\nexport type CheckoutEvent = (typeof CheckoutEvent)[keyof typeof CheckoutEvent];\n","import { CheckoutEvent } from '../enum/checkout-event.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/** Currency recorded when the caller does not specify one. */\nconst DEFAULT_CURRENCY = 'USD';\n\n/** Options for {@link CheckoutEvents.update}. */\nexport interface CheckoutEventUpdateOptions {\n /** The session whose checkout record is being advanced. */\n session: string;\n /** The funnel state now reached. */\n event: CheckoutEvent;\n /**\n * Additional context for this event - which upsell was shown, the order total,\n * a decline reason. Forwarded unmodified except that it can never override\n * `event`.\n */\n data?: Record<string, unknown>;\n}\n\n/**\n * Checkout events - the visitor's progress through the checkout funnel.\n *\n * Where {@link Sessions} records that a visit happened and {@link Carts} records\n * what was selected, this resource records what happened at the till: the\n * checkout page opening, each upsell offered and accepted or declined, and\n * finally whether the order was placed or declined. Together those events are\n * what makes checkout abandonment measurable.\n *\n * {@link CheckoutEvents.create} opens the record and always registers\n * `checkout_visited`; every later state goes through\n * {@link CheckoutEvents.update} with the matching {@link CheckoutEvent}. Neither\n * call can have its event overridden by the extra payload, so the funnel state\n * the server records is always the one the SDK's caller chose explicitly.\n */\nexport class CheckoutEvents extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Opens the checkout record for a session, registering `checkout_visited`.\n *\n * Call this once, when the visitor reaches the checkout page. The event is\n * fixed - this is the call that starts the funnel record - and `currency`\n * defaults to `USD` when you do not supply one, since the server requires a\n * currency to interpret any amounts in the payload.\n *\n * @param session The session reaching checkout.\n * @param data Additional context, e.g. `cart_total`, `items_count`,\n * `currency`. Cannot override `session` or `event`.\n * @returns The created checkout-event record.\n * @throws {ValidationError} If the session or payload is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async create<T = Record<string, unknown>>(\n session: string,\n data: Record<string, unknown> = {},\n ): Promise<ApiResponse<T>> {\n // Data first, fixed keys last: PHP's `+` keeps the left operand on a\n // conflict, so the payload must not be able to rewrite session or event.\n const body: Record<string, unknown> = {\n ...data,\n session,\n event: CheckoutEvent.CheckoutVisited,\n };\n\n if (body.currency === undefined || body.currency === '') {\n body.currency = DEFAULT_CURRENCY;\n }\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/checkout-events/create',\n body,\n });\n }\n\n /**\n * Advances a session's checkout record to a new funnel state.\n *\n * Send one call per state the visitor reaches: an upsell offered, then accepted\n * or declined, then the order placed or declined. The payload carries whatever\n * context that state needs, but it can never override `event` - the state\n * recorded is always the one you passed explicitly.\n *\n * @param options The session, the new state, and any context for it.\n * @returns The updated checkout-event record.\n * @throws {NotFoundError} If no checkout record exists for the session.\n * @throws {ValidationError} If the event or payload is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async update<T = Record<string, unknown>>(options: CheckoutEventUpdateOptions): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PUT',\n path: '/checkout-events/update/{session_id}',\n pathParams: { session_id: options.session },\n body: { ...(options.data ?? {}), event: options.event },\n });\n }\n}\n","import type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Doctors' networks - handing a case to the clinicians who will review it.\n *\n * A doctors' network is the panel of licensed clinicians configured for your\n * organisation. Syncing pushes a case to that panel so a clinician can pick it\n * up: it is the bridge between the commercial half of the order flow and the\n * clinical one.\n *\n * The single method here is unusual in the SDK for validating before it sends. A\n * sync with neither an opportunity to attach to nor enough detail to identify the\n * person would be accepted by the transport and rejected by the server, having\n * cost a round trip to learn something the SDK can see locally. So it checks\n * first, and raises `TypeError` rather than making the call.\n *\n * ```ts\n * await client.doctorsNetworks().sync({ opportunity_id: opportunityId });\n *\n * // or, without an opportunity yet:\n * await client.doctorsNetworks().sync({\n * user_info: { first_name: 'Jane', email: 'jane@example.test' },\n * });\n * ```\n */\nexport class DoctorsNetworks extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Pushes a case to the configured doctors' network.\n *\n * The payload must identify who the case is about, one of two ways: an\n * `opportunity_id` for a prospect already recorded, or a `user_info` object\n * carrying at least `first_name` and `email` for one who is not. Everything\n * else is forwarded unmodified.\n *\n * An `emit_opportunity` key is removed before sending - that flag is the\n * server's to set, not a caller's. Your object is not modified; the SDK copies\n * it first.\n *\n * @param payload The case to push. Requires either a non-empty\n * `opportunity_id` string, or a `user_info` object with non-empty\n * `first_name` and `email` strings.\n * @returns The sync result.\n * @throws {TypeError} If the payload identifies neither an opportunity nor a\n * person.\n * @throws {ValidationError} If the server rejects a field.\n * @throws {NotFoundError} If the referenced opportunity does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async sync<T = Record<string, unknown>>(payload: Record<string, unknown>): Promise<ApiResponse<T>> {\n // Copy before deleting: PHP's unset acts on a by-value array copy, but in JS\n // this object belongs to the caller.\n const body = { ...payload };\n delete body.emit_opportunity;\n\n validate(body);\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/doctors-networks/sync',\n body,\n });\n }\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value !== '';\n}\n\n/**\n * Requires the payload to identify the case's subject one of the two accepted\n * ways.\n *\n * @throws {TypeError} If it identifies neither.\n */\nfunction validate(payload: Record<string, unknown>): void {\n const hasOpportunity = isNonEmptyString(payload.opportunity_id);\n\n const userInfo = payload.user_info;\n const hasMinimalUserInfo =\n typeof userInfo === 'object' &&\n userInfo !== null &&\n isNonEmptyString((userInfo as Record<string, unknown>).first_name) &&\n isNonEmptyString((userInfo as Record<string, unknown>).email);\n\n if (!hasOpportunity && !hasMinimalUserInfo) {\n throw new TypeError('doctors-networks/sync requires either opportunity_id or user_info with first_name + email.');\n }\n}\n","import type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Geo - where a visitor is, and whether you are allowed to serve them.\n *\n * Telehealth is licensed jurisdiction by jurisdiction, so location is not a\n * personalisation detail here - it decides whether an order flow may start at\n * all. These two endpoints answer the two different questions that follow from\n * that.\n *\n * {@link Geo.info} resolves an IP address to a location, which is what you want\n * for pre-filling a state selector or routing a visitor to the right regional\n * content. {@link Geo.blocklist} answers the narrower question of whether this\n * address is barred - a jurisdiction you are not licensed in, a known proxy or\n * anonymiser. Check the blocklist at the top of the funnel: turning a visitor\n * away on the landing page is far cheaper than doing it after they have completed\n * an intake questionnaire.\n *\n * Pass the *visitor's* address, not your server's. Behind a proxy or load\n * balancer that means reading the forwarded-for header your infrastructure sets,\n * with the same care you would apply anywhere else - that header is\n * caller-supplied unless your edge overwrites it.\n *\n * Both take their input as a query parameter, and redaction never covers the URL,\n * so a debug entry for these calls contains the IP address that was checked.\n */\nexport class Geo extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Resolves an IP address to a geographic location.\n *\n * Returns the configured provider's answer, normalised. Treat it as a strong\n * hint rather than proof - a VPN or a mobile carrier's routing can place a\n * visitor a long way from where they are. For a licensing decision, prefer\n * {@link Geo.blocklist} and the state the visitor declares.\n *\n * @param ip The visitor's IP address, v4 or v6.\n * @returns The resolved location.\n * @throws {ValidationError} If the address is malformed.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async info<T = Record<string, unknown>>(ip: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'platform',\n method: 'GET',\n path: '/extensions/geo-info',\n query: { ip },\n });\n }\n\n /**\n * Reports whether an IP address is barred from starting an order flow.\n *\n * Covers jurisdictions your organisation is not licensed for, along with known\n * proxies and anonymisers. Call it as early as the landing page: refusing a\n * visitor before they invest time in a questionnaire is better for them and\n * cheaper for you.\n *\n * @param ip The visitor's IP address, v4 or v6.\n * @returns The blocklist verdict.\n * @throws {ValidationError} If the address is malformed.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async blocklist<T = Record<string, unknown>>(ip: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'platform',\n method: 'GET',\n path: '/extensions/geo-blocklist',\n query: { ip },\n });\n }\n}\n","import { readFile, stat } from 'node:fs/promises';\nimport { basename, extname } from 'node:path';\n\n/**\n * Extension-to-MIME map covering the types the file endpoints accept.\n *\n * An explicit table rather than content sniffing, because sniffing misreports\n * some of these: a `.docx` is a ZIP container on disk and sniffs as\n * `application/zip`, which the server rejects.\n */\nconst MIME_TYPES: Readonly<Record<string, string>> = Object.freeze({\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n webp: 'image/webp',\n gif: 'image/gif',\n pdf: 'application/pdf',\n doc: 'application/msword',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n txt: 'text/plain',\n mp4: 'video/mp4',\n mov: 'video/quicktime',\n webm: 'video/webm',\n});\n\n/** MIME type used when the file name does not identify the content. */\nconst FALLBACK_MIME_TYPE = 'application/octet-stream';\n\n/** Overrides accepted by {@link FileUpload.fromPath}. */\nexport interface FileUploadFromPathOptions {\n /** Name to send in the part header. Defaults to the path's base name. */\n fileName?: string;\n /** MIME type to send. Defaults to one derived from the extension. */\n mimeType?: string;\n}\n\n/** Overrides accepted by {@link FileUpload.fromContents}. */\nexport interface FileUploadFromContentsOptions {\n /** MIME type to send. Defaults to one derived from the file name. */\n mimeType?: string;\n}\n\n/**\n * An immutable file, in memory, ready to be sent as a `multipart/form-data`\n * part.\n *\n * A handful of AsterMD endpoints take bytes rather than JSON - the\n * intake-submission file endpoints on the `sales` service are the current\n * example. Those bytes come from very different places: a file your users just\n * uploaded and that now sits on local disk, or a slice of a large file being fed\n * through the multipart flow one chunk at a time. This class is the single shape\n * resource methods accept for both, so {@link Transport} has exactly one thing\n * to encode.\n *\n * Build it through one of the two factories - the constructor is private, so an\n * instance always carries a file name and a MIME type:\n *\n * ```ts\n * // From a file on disk; name and MIME type come from the path.\n * await client.intakeSubmissions().uploadFile(\n * sessionId,\n * await FileUpload.fromPath('/var/uploads/id-front.jpg'),\n * );\n *\n * // From bytes already in memory: one 10 MB slice of a large video.\n * await client.intakeSubmissions().uploadMultipartPart({\n * uploadId,\n * partNumber: 1,\n * part: FileUpload.fromContents(chunk, 'consult-video.mp4'),\n * });\n * ```\n *\n * The whole file is held in memory, so it is deliberately not the right tool for\n * a 200 MB video - use {@link IntakeSubmissions.uploadLargeFile}, which streams\n * from disk and only ever materialises one part at a time.\n *\n * File names are normalised on construction: directory components are dropped\n * and quote, backslash, and newline characters removed, so a caller-supplied\n * name can never break out of the header it is written into.\n */\nexport class FileUpload {\n readonly #contents: Uint8Array;\n readonly #fileName: string;\n readonly #mimeType: string;\n\n private constructor(contents: Uint8Array, fileName: string, mimeType: string) {\n this.#contents = contents;\n this.#fileName = fileName;\n this.#mimeType = mimeType;\n\n Object.freeze(this);\n }\n\n /**\n * Reads a file from local disk into an upload.\n *\n * The file is read in full and held in memory, so keep this for the sizes the\n * single-shot endpoints allow. Without `options.fileName` the path's base name\n * is used, and without `options.mimeType` the type is derived from the\n * extension, falling back to `application/octet-stream`.\n *\n * @param path Path to an existing, readable file.\n * @param options Optional name and MIME type overrides.\n * @returns An upload carrying the file's bytes, name, and MIME type.\n * @throws {TypeError} If `path` is not a readable file, or if the resulting\n * file name normalises to nothing usable.\n */\n static async fromPath(path: string, options: FileUploadFromPathOptions = {}): Promise<FileUpload> {\n let isFile: boolean;\n\n try {\n isFile = (await stat(path)).isFile();\n } catch {\n isFile = false;\n }\n\n if (!isFile) {\n throw new TypeError(`File is not readable: ${path}`);\n }\n\n let contents: Buffer;\n\n try {\n contents = await readFile(path);\n } catch (error) {\n throw new TypeError(`Failed to read file: ${path}`, { cause: error });\n }\n\n const name = normaliseFileName(options.fileName ?? basename(path));\n\n return new FileUpload(new Uint8Array(contents), name, options.mimeType ?? guessMimeType(name));\n }\n\n /**\n * Wraps bytes already held in memory into an upload.\n *\n * Use this when the bytes never came from a file on disk: a slice of a larger\n * file being sent through the multipart flow, a payload received over the\n * wire, or content generated at runtime. `fileName` is required because there\n * is no path to derive one from.\n *\n * @param contents The raw bytes to send as the part body.\n * @param fileName Name to send in the part header.\n * @param options Optional MIME type override.\n * @returns An upload carrying the given bytes, name, and MIME type.\n * @throws {TypeError} If `fileName` normalises to nothing usable.\n */\n static fromContents(contents: Uint8Array, fileName: string, options: FileUploadFromContentsOptions = {}): FileUpload {\n const name = normaliseFileName(fileName);\n\n return new FileUpload(contents, name, options.mimeType ?? guessMimeType(name));\n }\n\n /**\n * Returns the raw bytes that will form the body of the multipart part.\n *\n * @returns The file's contents.\n */\n contents(): Uint8Array {\n return this.#contents;\n }\n\n /**\n * Returns the normalised file name sent in the part header.\n *\n * The server uses this to derive the stored object name, inserting a unique\n * identifier before the extension so repeat uploads of the same name never\n * collide.\n *\n * @returns The file name, stripped of directory components and\n * header-breaking characters.\n */\n fileName(): string {\n return this.#fileName;\n }\n\n /**\n * Returns the MIME type sent for the part.\n *\n * @returns The MIME type, e.g. `image/jpeg`.\n */\n mimeType(): string {\n return this.#mimeType;\n }\n\n /**\n * Returns the size of the upload in bytes.\n *\n * Useful for checking a file against an endpoint's documented limit before\n * spending a round trip on it, and for the `fileSize` value the multipart\n * initiate call requires.\n *\n * @returns The number of bytes in {@link FileUpload.contents}.\n */\n size(): number {\n return this.#contents.byteLength;\n }\n}\n\n/**\n * Drops directory components, then strips the characters that would let a\n * caller-supplied name terminate or forge the part header it is written into.\n *\n * @throws {TypeError} If nothing usable remains.\n */\nfunction normaliseFileName(fileName: string): string {\n const stripped = fileName.trim().replace(/[\\r\\n\"\\\\]/g, '');\n const name = basename(stripped).trim();\n\n if (name === '' || name === '.' || name === '..') {\n throw new TypeError('File name must not be empty.');\n }\n\n return name;\n}\n\nfunction guessMimeType(fileName: string): string {\n const extension = extname(fileName).replace(/^\\./, '').toLowerCase();\n\n return MIME_TYPES[extension] ?? FALLBACK_MIME_TYPE;\n}\n","import { open, stat } from 'node:fs/promises';\nimport type { FileHandle } from 'node:fs/promises';\nimport { basename } from 'node:path';\nimport type { Event } from '../enum/event.js';\nimport { ApiError } from '../errors/index.js';\nimport { FileUpload } from '../http/file-upload.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/** Options for {@link IntakeSubmissions.view}. */\nexport interface IntakeViewOptions {\n /** The intake submission's id. */\n id: string;\n /** The teleform the submission answers. */\n teleformId: string;\n}\n\n/** Options for {@link IntakeSubmissions.create} and {@link IntakeSubmissions.update}. */\nexport interface IntakeWriteOptions {\n /** The session the submission belongs to. */\n session: string;\n /** The journey event this call records. */\n event: Event;\n /** The teleform being answered. */\n teleformId: string;\n /**\n * The answers, one entry per field: `{ id, name, label, type, value }`, where\n * `value` is itself a list of `{ value }` entries so a multi-select field can\n * carry several. Send every answer collected so far, not only what changed.\n */\n data: Record<string, unknown>[];\n /**\n * Optional progress information for a partially completed form, e.g.\n * `{ current_step, total_steps }`. Lets the server report how far a prospect\n * got before abandoning.\n */\n progress?: Record<string, unknown>;\n}\n\n/** Options for {@link IntakeSubmissions.uploadFile}. */\nexport interface UploadFileOptions {\n /** The session the file belongs to. */\n sessionId: string;\n /** The file to upload. */\n file: FileUpload;\n}\n\n/** Options for {@link IntakeSubmissions.initiateMultipartUpload}. */\nexport interface InitiateMultipartOptions {\n /** The session the file belongs to. */\n sessionId: string;\n /** Name to store the file under. */\n fileName: string;\n /** MIME type of the whole file. */\n mimeType: string;\n /** Total size of the whole file in bytes, not of one part. */\n fileSize: number;\n}\n\n/** Options for {@link IntakeSubmissions.uploadMultipartPart}. */\nexport interface UploadPartOptions {\n /** The upload id returned by initiate. */\n uploadId: string;\n /** 1-based part number, between 1 and 10000. */\n partNumber: number;\n /** The bytes of this part. */\n part: FileUpload;\n}\n\n/** One uploaded part, as {@link IntakeSubmissions.finishMultipartUpload} expects it. */\nexport interface MultipartPart {\n /** The part number that was uploaded. */\n part_number: number;\n /** The etag the server returned for that part. */\n etag: string;\n}\n\n/** Options for {@link IntakeSubmissions.finishMultipartUpload}. */\nexport interface FinishMultipartOptions {\n /** The upload id returned by initiate. */\n uploadId: string;\n /** Every uploaded part with its etag, in order. Must not be empty. */\n parts: MultipartPart[];\n}\n\n/** Options for {@link IntakeSubmissions.uploadLargeFile}. */\nexport interface UploadLargeFileOptions {\n /** The session the file belongs to. */\n sessionId: string;\n /** Path to the file on local disk. */\n filePath: string;\n /** Name to store the file under. Defaults to the path's base name. */\n fileName?: string;\n /** MIME type to send. Defaults to one derived from the file name. */\n mimeType?: string;\n /**\n * Bytes per part. Must be between {@link IntakeSubmissions.MIN_PART_SIZE} and\n * {@link IntakeSubmissions.MAX_PART_SIZE}. Defaults to\n * {@link IntakeSubmissions.DEFAULT_PART_SIZE}.\n */\n partSize?: number;\n}\n\n/**\n * Intake submissions - the answers a prospect gives to your questionnaires.\n *\n * This is where the clinical part of the order flow happens. Two questionnaires\n * run through this one resource: the short pre-qualifying form that decides\n * whether a prospect is eligible at all, and the full intake questionnaire a\n * clinician later reviews. Which one a call belongs to is carried entirely by the\n * {@link Event} it passes.\n *\n * Both follow the same rhythm. The `*Initiated` event goes to\n * {@link IntakeSubmissions.create}, which creates the server-side record. Every\n * later step goes to {@link IntakeSubmissions.update} with `*InProgress`, and the\n * final submission with `*Completed`. Sending progress updates as the prospect\n * moves through the form is what makes partial completion visible instead of\n * looking like an abandoned session:\n *\n * ```ts\n * await client.intakeSubmissions().create({\n * session,\n * event: Event.IntakeInitiated,\n * teleformId,\n * data: answersSoFar,\n * });\n *\n * await client.intakeSubmissions().update({\n * session,\n * event: Event.IntakeCompleted,\n * teleformId,\n * data: allAnswers,\n * });\n * ```\n *\n * `data` is cumulative: send every answer collected so far on each call, not just\n * the fields that changed. Answers that are files rather than values go through\n * the upload methods on this same resource.\n */\nexport class IntakeSubmissions extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Fetches one intake submission and its recorded answers.\n *\n * The teleform id is required alongside the submission id because a submission\n * is only meaningful against the form it answers - the server uses it to\n * resolve field definitions for the stored values.\n *\n * @param options The submission id and the teleform it answers.\n * @returns The submission and its answers.\n * @throws {NotFoundError} If the submission does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(options: IntakeViewOptions): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/intake-submissions/view/{id}',\n pathParams: { id: options.id },\n query: { teleform_id: options.teleformId },\n });\n }\n\n /**\n * Creates the intake-submission record for a session and records the first\n * answers.\n *\n * This is the call that opens the record, so it takes an `*Initiated` event -\n * `Event.PreQualifyingInitiated` or `Event.IntakeInitiated`. Every later step\n * goes to {@link IntakeSubmissions.update}. The server advances its order-flow\n * state machine on the event, so passing the wrong one puts the flow in the\n * wrong state rather than failing loudly.\n *\n * @param options The session, event, teleform, answers, and optional progress.\n * @returns The created submission.\n * @throws {ValidationError} If the answers or the event are rejected.\n * @throws {NotFoundError} If the session or teleform does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async create<T = Record<string, unknown>>(options: IntakeWriteOptions): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/intake-submissions/create',\n body: intakeBody(options),\n });\n }\n\n /**\n * Records further answers against an existing intake submission.\n *\n * Takes an `*InProgress` event for an intermediate step or a `*Completed` event\n * for the final submission. `data` is cumulative - send every answer collected\n * so far, since the server treats the payload as the current state of the form\n * rather than a patch.\n *\n * @param options The session, event, teleform, answers, and optional progress.\n * @returns The updated submission.\n * @throws {ValidationError} If the answers or the event are rejected.\n * @throws {NotFoundError} If no submission exists for the session.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async update<T = Record<string, unknown>>(options: IntakeWriteOptions): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PUT',\n path: '/intake-submissions/update/{session}',\n pathParams: { session: options.session },\n body: intakeBody(options),\n });\n }\n\n /** Smallest part the multipart endpoint accepts: 5 MB. */\n static readonly MIN_PART_SIZE = 5 * 1024 * 1024;\n\n /** Largest part the multipart endpoint accepts: 25 MB. */\n static readonly MAX_PART_SIZE = 25 * 1024 * 1024;\n\n /** Part size {@link IntakeSubmissions.uploadLargeFile} uses by default: 10 MB. */\n static readonly DEFAULT_PART_SIZE = 10 * 1024 * 1024;\n\n /**\n * Uploads one file in a single request, for a file-type answer.\n *\n * Use this for the sizes a single request can carry - a photograph of an ID, a\n * PDF. The whole file is held in memory and sent as one `multipart/form-data`\n * body. For anything large enough to risk a timeout, use\n * {@link IntakeSubmissions.uploadLargeFile} instead.\n *\n * The response carries the stored file's reference, which is what you place in\n * the `value` of the corresponding field when you next call\n * {@link IntakeSubmissions.update}.\n *\n * @param options The session and the file.\n * @returns The stored file's reference.\n * @throws {ValidationError} If the file type or size is rejected.\n * @throws {NotFoundError} If the session does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async uploadFile<T = Record<string, unknown>>(options: UploadFileOptions): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/intake-submissions/upload-file/{session_id}',\n pathParams: { session_id: options.sessionId },\n file: options.file,\n });\n }\n\n /**\n * Opens a multipart upload and returns the id the parts will be sent under.\n *\n * The first of the four low-level multipart calls. Declare the *whole* file's\n * name, type, and size here - the server uses the size to validate the parts it\n * later receives. Prefer {@link IntakeSubmissions.uploadLargeFile}, which\n * drives all four calls; reach for these directly only when you need to\n * control the loop, for instance to upload parts from several workers.\n *\n * @param options The session and the whole file's metadata.\n * @returns The opened upload; `data().upload_id` carries the id.\n * @throws {ValidationError} If the metadata is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async initiateMultipartUpload<T = Record<string, unknown>>(\n options: InitiateMultipartOptions,\n ): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/intake-submissions/upload-file-multipart/initiate/{session_id}',\n pathParams: { session_id: options.sessionId },\n body: {\n file_name: options.fileName,\n mime_type: options.mimeType,\n file_size: options.fileSize,\n },\n });\n }\n\n /**\n * Uploads one part of an open multipart upload.\n *\n * Part numbers are 1-based and must arrive with the part they label; the\n * response carries an etag you must keep, because\n * {@link IntakeSubmissions.finishMultipartUpload} needs every part's etag to\n * assemble the file. Parts may be uploaded in any order.\n *\n * @param options The upload id, the part number, and the part's bytes.\n * @returns The stored part; `data().etag` carries the etag.\n * @throws {TypeError} If `partNumber` is outside 1–10000.\n * @throws {NotFoundError} If the upload id is unknown or already finished.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async uploadMultipartPart<T = Record<string, unknown>>(options: UploadPartOptions): Promise<ApiResponse<T>> {\n if (options.partNumber < 1 || options.partNumber > 10_000) {\n throw new TypeError(`Part number must be between 1 and 10000, got ${options.partNumber}.`);\n }\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/intake-submissions/upload-file-multipart/part/{upload_id}',\n pathParams: { upload_id: options.uploadId },\n query: { part_number: options.partNumber },\n file: options.part,\n });\n }\n\n /**\n * Closes a multipart upload, assembling the parts into one stored file.\n *\n * Send every part with the etag its own upload returned. The server verifies\n * the set against what it received, so a missing or mismatched part fails here\n * rather than producing a corrupt file.\n *\n * @param options The upload id and every part with its etag.\n * @returns The stored file's reference.\n * @throws {TypeError} If `parts` is empty.\n * @throws {ValidationError} If the parts do not match what the server holds.\n * @throws {NotFoundError} If the upload id is unknown.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async finishMultipartUpload<T = Record<string, unknown>>(options: FinishMultipartOptions): Promise<ApiResponse<T>> {\n if (options.parts.length === 0) {\n throw new TypeError('At least one part is required to finish a multipart upload.');\n }\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/intake-submissions/upload-file-multipart/finish/{upload_id}',\n pathParams: { upload_id: options.uploadId },\n body: { parts: options.parts },\n });\n }\n\n /**\n * Abandons a multipart upload and discards the parts already stored.\n *\n * Call this when an upload cannot be completed, so the parts do not linger.\n * {@link IntakeSubmissions.uploadLargeFile} does it for you on failure.\n *\n * @param uploadId The upload id to abandon.\n * @returns The abort confirmation.\n * @throws {NotFoundError} If the upload id is unknown or already finished.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async abortMultipartUpload<T = Record<string, unknown>>(uploadId: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/intake-submissions/upload-file-multipart/abort/{upload_id}',\n pathParams: { upload_id: uploadId },\n });\n }\n\n /**\n * Uploads a large file from disk, driving the whole multipart flow.\n *\n * This is the method to reach for when a file is too big for one request. It\n * streams the file one part at a time, so a 200 MB video costs one part of\n * memory rather than 200 MB: initiate, then read a part, upload it, keep its\n * etag, repeat, then finish. If any step fails the upload is aborted so no\n * parts are left behind, and the failure that broke the upload is what\n * surfaces - not the abort's own outcome.\n *\n * The first part fixes the name and MIME type for the whole upload, so\n * `initiate` and every part agree.\n *\n * ```ts\n * const stored = await client.intakeSubmissions().uploadLargeFile({\n * sessionId,\n * filePath: '/var/uploads/consult-video.mp4',\n * });\n * ```\n *\n * @param options The session, the file's path, and optional name, MIME type,\n * and part size.\n * @returns The finished upload's response - the same one\n * {@link IntakeSubmissions.finishMultipartUpload} returns.\n * @throws {TypeError} If `partSize` is outside the allowed range, or if the\n * file is missing, unreadable, or empty.\n * @throws {ApiError} If the server response omits a usable `upload_id` or\n * `etag`, or on any other non-2xx status.\n * @throws {TransportError} If a request never completed.\n */\n async uploadLargeFile<T = Record<string, unknown>>(options: UploadLargeFileOptions): Promise<ApiResponse<T>> {\n const partSize = options.partSize ?? IntakeSubmissions.DEFAULT_PART_SIZE;\n\n if (partSize < IntakeSubmissions.MIN_PART_SIZE || partSize > IntakeSubmissions.MAX_PART_SIZE) {\n throw new TypeError(\n `Part size must be between ${IntakeSubmissions.MIN_PART_SIZE} and ` +\n `${IntakeSubmissions.MAX_PART_SIZE} bytes, got ${partSize}.`,\n );\n }\n\n let fileSize: number;\n\n try {\n const stats = await stat(options.filePath);\n\n if (!stats.isFile()) {\n throw new TypeError(`File is not readable: ${options.filePath}`);\n }\n\n fileSize = stats.size;\n } catch (error) {\n if (error instanceof TypeError) {\n throw error;\n }\n\n throw new TypeError(`File is not readable: ${options.filePath}`, {\n cause: error,\n });\n }\n\n if (fileSize === 0) {\n throw new TypeError(`File is empty: ${options.filePath}`);\n }\n\n const handle = await open(options.filePath, 'r');\n\n try {\n const buffer = Buffer.alloc(partSize);\n const firstBytesRead = await readFull(handle, buffer, partSize);\n\n if (firstBytesRead === 0) {\n throw new TypeError(`File is empty: ${options.filePath}`);\n }\n\n // The first part fixes the name and MIME type for the whole upload, so\n // initiate agrees with every part that follows it.\n //\n // Copy the chunk out of the reused read buffer. `Buffer.prototype.slice`\n // is an alias for `subarray` and returns a view, not a copy. Today that\n // would still be safe, because Transport snapshots each part into a Blob\n // before the next read refills the buffer - but the copy keeps this\n // correct independently of send ordering, so a later change that reads\n // ahead or batches parts cannot silently ship the wrong bytes.\n let part = FileUpload.fromContents(\n new Uint8Array(buffer.subarray(0, firstBytesRead)),\n options.fileName ?? basename(options.filePath),\n options.mimeType === undefined ? {} : { mimeType: options.mimeType },\n );\n\n const uploadId = stringField(\n await this.initiateMultipartUpload({\n sessionId: options.sessionId,\n fileName: part.fileName(),\n mimeType: part.mimeType(),\n fileSize,\n }),\n 'upload_id',\n );\n\n try {\n const parts: MultipartPart[] = [];\n let partNumber = 1;\n\n for (;;) {\n const result = await this.uploadMultipartPart({\n uploadId,\n partNumber,\n part,\n });\n parts.push({\n part_number: partNumber,\n etag: stringField(result, 'etag'),\n });\n\n const nextBytesRead = await readFull(handle, buffer, partSize);\n\n if (nextBytesRead === 0) {\n break;\n }\n\n partNumber += 1;\n // Same reused buffer as above: copy the chunk out for the same reason\n // - safe today because of send ordering, but not something to rely on.\n part = FileUpload.fromContents(new Uint8Array(buffer.subarray(0, nextBytesRead)), part.fileName(), {\n mimeType: part.mimeType(),\n });\n }\n\n return await this.finishMultipartUpload<T>({ uploadId, parts });\n } catch (error) {\n try {\n await this.abortMultipartUpload(uploadId);\n } catch {\n // Surface the failure that broke the upload, not the cleanup's.\n }\n\n throw error;\n }\n } finally {\n await handle.close().catch(() => undefined);\n }\n }\n}\n\n/**\n * Reads until the buffer is full or the file ends.\n *\n * A single `read()` may return fewer bytes than asked for even mid-file - POSIX\n * permits it and network and overlay filesystems do it - which would produce a\n * part below the minimum size and fail the upload only at the finish step.\n */\nasync function readFull(handle: FileHandle, buffer: Buffer, length: number): Promise<number> {\n let filled = 0;\n\n while (filled < length) {\n const { bytesRead } = await handle.read(buffer, filled, length - filled, null);\n\n if (bytesRead === 0) {\n break;\n }\n\n filled += bytesRead;\n }\n\n return filled;\n}\n\n/**\n * Builds the request body shared by create and update.\n *\n * The session appears in the body as well as the path on update; that is what the\n * server expects, so it is not redundant.\n */\nfunction intakeBody(options: IntakeWriteOptions): Record<string, unknown> {\n const body: Record<string, unknown> = {\n session: options.session,\n event: options.event,\n teleform_id: options.teleformId,\n data: options.data,\n };\n\n if (options.progress !== undefined) {\n body.progress = options.progress;\n }\n\n return body;\n}\n\n/**\n * Reads a required string out of a response payload.\n *\n * The multipart flow depends on two server-supplied values - the upload id and\n * each part's etag - and continuing without one produces a corrupt upload rather\n * than an error. So a missing or empty value fails here, loudly.\n */\nfunction stringField(response: ApiResponse, key: string): string {\n const value = response.data()[key];\n\n if (typeof value !== 'string' || value === '') {\n throw new ApiError(`The server response did not include a usable ${key}.`, response.statusCode(), {});\n }\n\n return value;\n}\n","import type { QueryParams } from '../http/url-builder.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Lab tests - the diagnostic panels a product may require.\n *\n * Some treatments cannot be prescribed without a result: a blood panel, a\n * hormone level. A lab test is the server-side definition of one such\n * requirement, and products reference the tests they depend on. A storefront reads\n * this to tell a buyer, before they pay, that a test will be part of their\n * treatment.\n *\n * Read-only, and configured in your AsterMD dashboard.\n */\nexport class LabTests extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Lists lab tests, filtered and paginated.\n *\n * Pagination details come back in `meta()`. The accepted filters are in the\n * API reference in your AsterMD dashboard.\n *\n * @param query Filters and pagination parameters.\n * @returns The matching lab tests, with pagination in `meta()`.\n * @throws {ValidationError} If a filter is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async list<T = Record<string, unknown>>(query: QueryParams = {}): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/lab-tests/list',\n query,\n });\n }\n\n /**\n * Fetches one lab test.\n *\n * @param id The lab test's id.\n * @returns The lab test record.\n * @throws {NotFoundError} If the lab test does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/lab-tests/view/{id}',\n pathParams: { id },\n });\n }\n}\n","import type { QueryParams } from '../http/url-builder.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Medications - the reference list of drugs the platform knows about.\n *\n * This is a lookup table, not a catalogue of things to sell: it is what a\n * storefront renders when an intake form asks \"which medications are you\n * currently taking?\" Answers picked from this list arrive at the clinician as\n * recognised drugs rather than free text, which is the difference between a\n * reviewable history and a paragraph someone has to interpret.\n *\n * List-only by design - the API exposes no single-medication endpoint, so\n * neither does the SDK. Filter through the query parameters to implement a\n * type-ahead rather than downloading the whole list.\n */\nexport class Medications extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Lists medications, filtered and paginated.\n *\n * Intended to back a search or type-ahead on an intake form. Pagination comes\n * back in `meta()`; the accepted filters are in the API reference in your\n * AsterMD dashboard.\n *\n * @param query Filters and pagination parameters.\n * @returns The matching medications, with pagination in `meta()`.\n * @throws {ValidationError} If a filter is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async list<T = Record<string, unknown>>(query: QueryParams = {}): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/medications/list',\n query,\n });\n }\n}\n","import type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Opportunities - a prospect who has identified themselves and is worth\n * following up.\n *\n * This is the commercial record of the order flow, and the point where an\n * anonymous session becomes a named person. Create it once you have a name and a\n * contact address, typically right after the pre-qualifying form, and attach the\n * sessions that led to it so the whole journey stays connected:\n *\n * ```ts\n * const opportunity = await client.opportunities().create({\n * first_name: 'Jane',\n * email: 'jane@example.test',\n * sessions: [session],\n * });\n * ```\n *\n * Attaching sessions is what makes attribution work: without them the server has\n * a lead with no idea where it came from. An opportunity can carry several\n * sessions, which is what you want when a prospect returns across devices or\n * days.\n *\n * Where this record is commercial, {@link Patients} is its clinical counterpart -\n * created later, once the prospect is actually proceeding with treatment.\n */\nexport class Opportunities extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Creates an opportunity.\n *\n * Include a `sessions` array of the session identifiers that led here, so the\n * server can attribute the lead. The rest of the payload is forwarded\n * unmodified.\n *\n * @param data The prospect's details and the sessions to attach.\n * @returns The created opportunity, including its id.\n * @throws {ValidationError} If a field is missing or rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async create<T = Record<string, unknown>>(data: Record<string, unknown>): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/opportunities/create',\n body: data,\n });\n }\n\n /**\n * Fetches one opportunity.\n *\n * @param id The opportunity's id.\n * @returns The opportunity record.\n * @throws {NotFoundError} If the opportunity does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/opportunities/view/{id}',\n pathParams: { id },\n });\n }\n\n /**\n * Updates an opportunity.\n *\n * Send only the fields you are changing. Use this to enrich a lead as more\n * information arrives - a phone number, a later session to attach.\n *\n * @param id The opportunity's id.\n * @param data The fields to change.\n * @returns The updated opportunity.\n * @throws {NotFoundError} If the opportunity does not exist.\n * @throws {ValidationError} If a field is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async update<T = Record<string, unknown>>(id: string, data: Record<string, unknown>): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PUT',\n path: '/opportunities/update/{id}',\n pathParams: { id },\n body: data,\n });\n }\n\n /**\n * Changes an opportunity's status.\n *\n * Kept separate from {@link Opportunities.update} so a pipeline transition is\n * explicit in your code and in the server's audit trail. The accepted values\n * are configured for your organisation; see the API reference in your AsterMD\n * dashboard.\n *\n * @param id The opportunity's id.\n * @param status The new status value, sent as `opportunity_status`.\n * @returns The updated opportunity.\n * @throws {NotFoundError} If the opportunity does not exist.\n * @throws {ValidationError} If the status is not an accepted value.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async status<T = Record<string, unknown>>(id: string, status: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PATCH',\n path: '/opportunities/status/{id}',\n pathParams: { id },\n body: { opportunity_status: status },\n });\n }\n}\n","import type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/** Options for {@link Patients.submitHealthInformation}. */\nexport interface SubmitHealthInformationOptions {\n /**\n * The health information to record. Forwarded unmodified; see the API\n * reference in your AsterMD dashboard for the accepted fields.\n */\n data: Record<string, unknown>;\n /**\n * Verification token proving the patient authorised this submission, sent as\n * the `x-phi-verification-token` header. Required by the server for\n * submissions that need explicit patient confirmation.\n */\n phiVerificationToken?: string;\n}\n\n/**\n * Patients - the person receiving care, and the most sensitive data in the SDK.\n *\n * A patient record is created once the prospect has qualified and is proceeding\n * with treatment, and it is what a clinician's review, a prescription, and an\n * order all hang from. Where an {@link Opportunities} record is a commercial\n * artifact, this is a clinical one.\n *\n * Everything on this resource carries personal and protected health information.\n * Two consequences worth knowing about:\n *\n * The SDK never writes a request or response body from any `patients/*` endpoint\n * to a debug log, even with `debugRedact: false` - those bodies are dropped\n * wholesale rather than masked. Debug entries for these calls show the method,\n * URL, and status only.\n *\n * Health-information submission is a two-step flow when the server requires\n * explicit patient confirmation: {@link Patients.verifyHealthInformationOtp}\n * confirms a one-time code the patient received, and the resulting token is\n * passed to {@link Patients.submitHealthInformation} as\n * `phiVerificationToken`.\n *\n * Handling this data does not by itself make your application HIPAA compliant -\n * that depends on your own infrastructure, policies, and agreements. Contact\n * info@astermd.com regarding a Business Associate Agreement.\n */\nexport class Patients extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Creates a patient record.\n *\n * Call this once the prospect has qualified and is proceeding. The payload is\n * forwarded unmodified, so it carries whatever demographic and contact fields\n * your organisation's configuration requires.\n *\n * @param data The patient's details. See the API reference in your AsterMD\n * dashboard for the accepted fields.\n * @returns The created patient; `data()` carries the new record including its id.\n * @throws {ValidationError} If a field is missing or rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async create<T = Record<string, unknown>>(data: Record<string, unknown>): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/patients/create',\n body: data,\n });\n }\n\n /**\n * Fetches one patient record.\n *\n * Returns the record as your credentials are permitted to see it. Because the\n * organisation scope lives inside the JWT, an id belonging to another\n * organisation is indistinguishable from one that never existed - both raise\n * {@link NotFoundError}.\n *\n * @param id The patient's id.\n * @returns The patient record.\n * @throws {NotFoundError} If the patient does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/patients/view/{id}',\n pathParams: { id },\n });\n }\n\n /**\n * Updates a patient record.\n *\n * Send only the fields you are changing; the server merges them into the\n * existing record rather than replacing it.\n *\n * @param id The patient's id.\n * @param data The fields to change.\n * @returns The updated patient record.\n * @throws {NotFoundError} If the patient does not exist.\n * @throws {ValidationError} If a field is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async update<T = Record<string, unknown>>(id: string, data: Record<string, unknown>): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PUT',\n path: '/patients/update/{id}',\n pathParams: { id },\n body: data,\n });\n }\n\n /**\n * Changes a patient's status.\n *\n * A narrow call for the one field that drives workflow state, kept separate\n * from {@link Patients.update} so a status transition is explicit in your code\n * and in the server's audit trail. The accepted values are configured for your\n * organisation; see the API reference in your AsterMD dashboard.\n *\n * @param id The patient's id.\n * @param status The new status value.\n * @returns The updated patient record.\n * @throws {NotFoundError} If the patient does not exist.\n * @throws {ValidationError} If the status is not an accepted value.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async status<T = Record<string, unknown>>(id: string, status: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PATCH',\n path: '/patients/status/{id}',\n pathParams: { id },\n body: { status },\n });\n }\n\n /**\n * Submits a patient's health information.\n *\n * This is the clinical questionnaire result a clinician reviews, and the most\n * sensitive payload the SDK carries. Where the server requires explicit patient\n * confirmation, obtain a token through\n * {@link Patients.verifyHealthInformationOtp} first and pass it as\n * `phiVerificationToken`.\n *\n * Neither the request nor the response body is ever written to a debug log.\n *\n * @param options The health information, and the verification token where one\n * is required.\n * @returns The stored submission.\n * @throws {AuthenticationError} If the verification token is missing or invalid.\n * @throws {ValidationError} If the payload is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async submitHealthInformation<T = Record<string, unknown>>(\n options: SubmitHealthInformationOptions,\n ): Promise<ApiResponse<T>> {\n const headers =\n options.phiVerificationToken !== undefined ? { 'x-phi-verification-token': options.phiVerificationToken } : {};\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/patients/health-information',\n body: options.data,\n headers,\n });\n }\n\n /**\n * Verifies the one-time code a patient was sent, before health information is\n * submitted.\n *\n * The confirming half of the two-step health-information flow. On success the\n * response carries the verification token to pass to\n * {@link Patients.submitHealthInformation}.\n *\n * The one-time code is a credential: it is never written to a debug log, and it\n * should not be written to yours either.\n *\n * @param data The patient identifier and the one-time code. See the API\n * reference in your AsterMD dashboard for the exact fields.\n * @returns The verification result, carrying the token on success.\n * @throws {ValidationError} If the code is wrong, expired, or malformed.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async verifyHealthInformationOtp<T = Record<string, unknown>>(\n data: Record<string, unknown>,\n ): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/patients/health-information/otp-verification',\n body: data,\n });\n }\n}\n","import type { QueryParams } from '../http/url-builder.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Products - the read-only catalogue your storefront sells from.\n *\n * A product carries everything a storefront needs to render it: names,\n * descriptions, images, variants, prices, purchase quantity limits, and the\n * questionnaire a buyer must complete. All of it is configured in your AsterMD\n * dashboard, and the SDK only reads it - a storefront never creates or edits a\n * product.\n *\n * Which products a given selling surface may offer is a channel decision, so\n * {@link Channels.assignedProducts} and {@link Channels.details} are usually the\n * better starting point for a catalogue page. Reach for this resource when you\n * need the full product list regardless of channel, or one product by id.\n *\n * Image and asset paths come back relative. Resolve them with\n * `client.config().assetUrl(path)`, and treat the result as a fetch URL: download\n * the asset once and serve your own copy rather than hot-linking.\n */\nexport class Products extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Lists products, filtered and paginated.\n *\n * Pagination details come back in `meta()`. The accepted filters are in the\n * API reference in your AsterMD dashboard.\n *\n * @param query Filters and pagination parameters.\n * @returns The matching products, with pagination in `meta()`.\n * @throws {ValidationError} If a filter is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async list<T = Record<string, unknown>>(query: QueryParams = {}): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/products/list',\n query,\n });\n }\n\n /**\n * Fetches one product, including its variants and prices.\n *\n * @param id The product's id.\n * @returns The product record.\n * @throws {NotFoundError} If the product does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/products/view/{id}',\n pathParams: { id },\n });\n }\n}\n","import type { Response as ApiResponse } from '../response.js';\nimport type { Transport } from '../http/transport.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/** Options for {@link Sessions.create}. */\nexport interface SessionCreateOptions {\n /**\n * Attribution and context to record against the session - UTM parameters,\n * referrer, landing page. Forwarded unmodified; see the API reference in your\n * AsterMD dashboard for the recognised keys.\n */\n data?: Record<string, unknown>;\n /** The visitor's user agent, forwarded as the `User-Agent` header. */\n userAgent?: string;\n /** The visitor's IP address, forwarded as the `X-Original-Client-Ip` header. */\n clientIp?: string;\n}\n\n/** Options for {@link Sessions.view}. */\nexport interface SessionViewOptions {\n /** Session identifiers to fetch. Sent as a comma-separated `session_ids` query. */\n sessions: string[];\n /** IANA time zone used to render timestamps in the response. */\n tz?: string;\n}\n\n/**\n * Sessions - the thread that ties one visitor's whole journey together.\n *\n * A session is the first thing an order flow creates and the value every later\n * call is attached to: intake submissions, the cart, checkout events, and finally\n * the opportunity. Creating one also records an implicit `visit_page` event\n * server-side, so the funnel starts measuring from that moment.\n *\n * Create the session as early as the visitor's first page view, keep the returned\n * identifier for the length of their visit - in a cookie or your own server-side\n * session - and pass it to every subsequent call:\n *\n * ```ts\n * const { session } = (\n * await client.sessions().create<{ session: string }>({\n * userAgent: request.headers['user-agent'],\n * clientIp: request.ip,\n * })\n * ).data();\n * ```\n *\n * Passing the visitor's user agent and IP matters: the server uses them for\n * attribution and geographic checks, and without them every session appears to\n * originate from your server rather than from the visitor.\n */\nexport class Sessions extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Creates a new session and returns its identifier.\n *\n * This is the first call of an order flow. The server records an implicit\n * `visit_page` event alongside the new session, so the funnel begins here.\n * Inspect `data().session` on the response for the identifier to thread through\n * every later call.\n *\n * @param options Attribution payload and visitor headers. All optional; with\n * none, an empty object body is sent.\n * @returns The created session; `data()` holds `{ session }`.\n * @throws {ValidationError} If the attribution payload is rejected.\n * @throws {AuthenticationError} If the credentials are refused.\n * @throws {RateLimitError} If the organisation's request allowance is exceeded.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async create<T = Record<string, unknown>>(options: SessionCreateOptions = {}): Promise<ApiResponse<T>> {\n const headers: Record<string, string> = {};\n\n if (options.userAgent !== undefined && options.userAgent !== '') {\n headers['User-Agent'] = options.userAgent;\n }\n\n if (options.clientIp !== undefined && options.clientIp !== '') {\n headers['X-Original-Client-Ip'] = options.clientIp;\n }\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/sessions/create',\n body: options.data ?? {},\n headers,\n });\n }\n\n /**\n * Fetches one or more sessions and the events recorded against them.\n *\n * Useful for reconstructing a visitor's journey server-side - which forms they\n * touched, where they stopped. Several identifiers can be fetched in one call,\n * which is cheaper than looping. Timestamps come back in `tz` when you supply\n * one, and in the organisation's default zone otherwise.\n *\n * @param options The session identifiers to fetch, and an optional time zone.\n * @returns The sessions and their recorded events.\n * @throws {NotFoundError} If none of the identifiers resolve.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(options: SessionViewOptions): Promise<ApiResponse<T>> {\n const query: Record<string, string> = {\n session_ids: options.sessions.join(','),\n };\n\n if (options.tz !== undefined && options.tz !== '') {\n query.tz = options.tz;\n }\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/sessions/view',\n query,\n });\n }\n\n /**\n * Updates the attribution and context recorded against an existing session.\n *\n * Call this when information arrives after the session was created - a UTM\n * parameter only present on a later page, a referrer resolved asynchronously.\n * The payload is merged server-side rather than replacing what is already\n * recorded.\n *\n * @param session The session identifier to update.\n * @param data Fields to record. Forwarded unmodified; see the API reference in\n * your AsterMD dashboard for the recognised keys.\n * @returns The updated session.\n * @throws {NotFoundError} If the session does not exist.\n * @throws {ValidationError} If the payload is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async update<T = Record<string, unknown>>(\n session: string,\n data: Record<string, unknown> = {},\n ): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PUT',\n path: '/sessions/update/{id}',\n pathParams: { id: session },\n body: data,\n });\n }\n\n /**\n * Deletes a session and the events recorded against it.\n *\n * Intended for honouring a visitor's erasure request, not for ordinary\n * cleanup - sessions age out server-side on their own. Deleting one removes the\n * attribution trail for anything that referenced it.\n *\n * @param session The session identifier to delete.\n * @returns The deletion confirmation.\n * @throws {NotFoundError} If the session does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async delete<T = Record<string, unknown>>(session: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'DELETE',\n path: '/sessions/delete/{id}',\n pathParams: { id: session },\n });\n }\n}\n","import type { QueryParams } from '../http/url-builder.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Shipping options - the delivery methods available for an order.\n *\n * Each option carries its name, price, and expected delivery window, which is\n * what a checkout page needs to let a buyer choose. Which options apply can depend\n * on the products in the cart and on the destination, so fetch them at checkout\n * rather than hardcoding a list.\n *\n * Read-only, and configured in your AsterMD dashboard.\n */\nexport class Shippings extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Lists shipping options, filtered and paginated.\n *\n * Pagination details come back in `meta()`. The accepted filters are in the\n * API reference in your AsterMD dashboard.\n *\n * @param query Filters and pagination parameters.\n * @returns The matching shipping options, with pagination in `meta()`.\n * @throws {ValidationError} If a filter is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async list<T = Record<string, unknown>>(query: QueryParams = {}): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/shippings/list',\n query,\n });\n }\n\n /**\n * Fetches one shipping option.\n *\n * @param id The shipping option's id.\n * @returns The shipping option record.\n * @throws {NotFoundError} If the shipping option does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/shippings/view/{id}',\n pathParams: { id },\n });\n }\n}\n","import type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Teleforms - the questionnaire definitions your storefront renders.\n *\n * A teleform is the server-side definition of a form: its fields, their types,\n * their order, and the validation the clinician's workflow expects. Both the\n * pre-qualifying questionnaire and the full intake questionnaire are teleforms.\n * Fetch the definition, render it in your own UI, and submit the answers through\n * {@link IntakeSubmissions} together with the teleform's id.\n *\n * There are two ways in. {@link Teleforms.viewByIdentifier} takes the stable\n * human-readable identifier you configured in your AsterMD dashboard, which is\n * what a storefront normally has hardcoded or in configuration.\n * {@link Teleforms.view} takes the generated id, which is what appears in API\n * responses elsewhere.\n *\n * Definitions change when someone edits the form in the dashboard, so fetch them\n * rather than embedding a copy - but they change rarely, so caching the result\n * for minutes rather than seconds is reasonable.\n */\nexport class Teleforms extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Fetches a teleform definition by its configured identifier.\n *\n * This is the call a storefront usually wants: the identifier is the stable\n * name you chose in your AsterMD dashboard, so it can live in your\n * configuration and survive the form being edited.\n *\n * @param identifier The teleform's configured identifier.\n * @returns The teleform definition.\n * @throws {NotFoundError} If no teleform carries that identifier.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async viewByIdentifier<T = Record<string, unknown>>(identifier: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/teleforms/view-url/{form_json_identifier}',\n pathParams: { form_json_identifier: identifier },\n });\n }\n\n /**\n * Fetches a teleform definition by its generated id.\n *\n * Use this when you already hold an id from another API response - a product's\n * assigned teleform, for instance - rather than a configured identifier.\n *\n * @param id The teleform's generated id.\n * @returns The teleform definition.\n * @throws {NotFoundError} If the teleform does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/teleforms/view/{id}',\n pathParams: { id },\n });\n }\n}\n","import type { QueryParams } from '../http/url-builder.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/** Options for {@link Treatments.sync}. */\nexport interface TreatmentSyncOptions {\n /** The session the orders belong to. */\n session: string;\n /** Order identifiers from your own commerce system. */\n orderIds: string[];\n /** Campaign attribution to record alongside the orders. */\n utmSource?: string;\n /** The visitor's user agent, forwarded as the `User-Agent` header. */\n userAgent?: string;\n}\n\n/**\n * Treatments - the order, once money has actually changed hands.\n *\n * A treatment is the end of the order flow and the start of the clinical one:\n * the record a clinician reviews, prescribes against, and that fulfilment works\n * from. Create it *after* your own payment flow has settled, never before - the\n * SDK does not process payments, and a treatment recorded against an unsettled\n * payment puts an order into the clinical queue that may never be paid for.\n *\n * There are two ways to record one. {@link Treatments.create} takes the order\n * explicitly, which is what you want when your storefront owns checkout.\n * {@link Treatments.sync} takes a session and a list of order identifiers from\n * your own commerce system, which is what you want when checkout happened\n * elsewhere and you are reconciling after the fact.\n *\n * ```ts\n * await client.treatments().create({\n * external_order_id: 'EXT-123',\n * patient: { id: patientId },\n * product: { id: productId },\n * });\n * ```\n */\nexport class Treatments extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Records a treatment - an order that has been paid for.\n *\n * Call this only after your payment flow has settled. Include your own order\n * identifier as `external_order_id` so the record can be reconciled against\n * your commerce system later.\n *\n * @param data The order: patient, product, your order identifier, and whatever\n * else your configuration requires.\n * @returns The created treatment.\n * @throws {ValidationError} If a field is missing or rejected.\n * @throws {NotFoundError} If the referenced patient or product does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async create<T = Record<string, unknown>>(data: Record<string, unknown>): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/treatments/create',\n body: data,\n });\n }\n\n /**\n * Fetches one treatment, including its clinical status.\n *\n * This is how a storefront shows an order's progress - whether a clinician has\n * reviewed it, whether it has shipped.\n *\n * @param id The treatment's id.\n * @returns The treatment record.\n * @throws {NotFoundError} If the treatment does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/treatments/view/{id}',\n pathParams: { id },\n });\n }\n\n /**\n * Lists treatments, filtered and paginated.\n *\n * Useful for an account area showing a patient's order history. Pagination\n * details come back in `meta()`; the accepted filters are in the API reference\n * in your AsterMD dashboard.\n *\n * @param query Filters and pagination parameters.\n * @returns The matching treatments, with pagination in `meta()`.\n * @throws {ValidationError} If a filter is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async list<T = Record<string, unknown>>(query: QueryParams = {}): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/treatments/list',\n query,\n });\n }\n\n /**\n * Reconciles orders placed in your own commerce system against a session.\n *\n * The alternative to {@link Treatments.create} for flows where checkout happens\n * outside your storefront: hand over the session and your order identifiers and\n * the server creates the treatments and attributes them to that session's\n * journey. Several orders can be reconciled in one call.\n *\n * @param options The session, your order identifiers, and optional attribution.\n * @returns The created treatments.\n * @throws {ValidationError} If the session or an order identifier is rejected.\n * @throws {NotFoundError} If the session does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async sync<T = Record<string, unknown>>(options: TreatmentSyncOptions): Promise<ApiResponse<T>> {\n const body: Record<string, unknown> = {\n session_id: options.session,\n order_ids: options.orderIds,\n };\n\n if (options.utmSource !== undefined) {\n body.utm_source = options.utmSource;\n }\n\n const headers =\n options.userAgent !== undefined && options.userAgent !== '' ? { 'User-Agent': options.userAgent } : {};\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/treatments/sync',\n body,\n headers,\n });\n }\n}\n","import type { IdentityCheck } from '../enum/identity-check.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Verification - checking that the details a prospect typed are real.\n *\n * Telehealth needs more confidence in an address, an email, and an identity than\n * ordinary commerce does: a prescription goes to a physical person at a physical\n * place. These endpoints proxy the provider configured for your organisation, and\n * normalise its answers, so your storefront gets one shape regardless of which\n * provider sits behind it.\n *\n * Address autofill and verification belong at the point of entry - autofill as\n * the buyer types, verification before checkout completes. Email verification\n * catches typos and disposable addresses before they cost you a lost patient.\n * Identity verification is the strongest check and is usually required before a\n * clinician will review a case; which variant applies is carried by\n * {@link IdentityCheck}.\n *\n * Note where these calls appear in debug logs. Redaction covers headers and\n * bodies but never the URL, and the address and email endpoints take their input\n * as a query parameter - so a debug entry for those calls contains the address or\n * email that was checked. That is worth knowing when deciding where debug logs\n * are written and how long they are kept. Identity verification is the exception:\n * it is a POST, and its body is dropped from logs entirely because it carries\n * PII.\n */\nexport class Verification extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Suggests complete addresses for a partial one.\n *\n * Built for a type-ahead: call it as the buyer types and render the\n * suggestions. Debouncing is your side's job - every keystroke here is a round\n * trip and counts against your request allowance.\n *\n * @param search The partial address typed so far.\n * @returns Candidate addresses from the configured provider.\n * @throws {ValidationError} If the search term is rejected.\n * @throws {RateLimitError} If the request allowance is exceeded.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async autofillAddress<T = Record<string, unknown>>(search: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'platform',\n method: 'GET',\n path: '/extensions/address-autofill',\n query: { search },\n });\n }\n\n /**\n * Verifies that an address exists and is deliverable.\n *\n * Call this before checkout completes rather than after: a prescription sent to\n * an undeliverable address is a clinical problem, not just a logistics one. The\n * response carries the provider's normalised form of the address, which is what\n * you should store.\n *\n * @param address The full address to verify.\n * @returns The verification result and the normalised address.\n * @throws {ValidationError} If the address is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async verifyAddress<T = Record<string, unknown>>(address: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'platform',\n method: 'GET',\n path: '/extensions/address-verify',\n query: { address },\n });\n }\n\n /**\n * Verifies that an email address is real and reachable.\n *\n * Catches typos and disposable addresses at the point of entry. Since every\n * later notification - intake reminders, clinician messages, shipping updates -\n * goes to this address, a bad one silently costs you the patient.\n *\n * @param email The email address to verify.\n * @returns The verification result.\n * @throws {ValidationError} If the address is malformed.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async verifyEmail<T = Record<string, unknown>>(email: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'platform',\n method: 'GET',\n path: '/extensions/email-verify',\n query: { email },\n });\n }\n\n /**\n * Runs an identity check against the configured provider.\n *\n * The strongest check available, and usually a precondition for clinical\n * review. Which variant runs is carried by `check`, and each expects a\n * different payload - see the API reference in your AsterMD dashboard for the\n * fields each one needs.\n *\n * The payload carries PII, and a Social Security Number in the `SsnVerify`\n * case. It is never written to a debug log, and it should not be written to\n * yours. The payload cannot override the check you selected.\n *\n * @param check Which identity check to run.\n * @param data The fields that check requires.\n * @returns The verification result.\n * @throws {ValidationError} If the payload does not satisfy the chosen check.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async verifyIdentity<T = Record<string, unknown>>(\n check: IdentityCheck,\n data: Record<string, unknown>,\n ): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'platform',\n method: 'POST',\n path: '/extensions/identity-verify',\n // Data first, slug last: the caller's payload must not rewrite the check.\n body: { ...data, slug: check },\n });\n }\n}\n","import { InMemoryTokenStore } from './auth/in-memory-token-store.js';\nimport { TokenManager } from './auth/token-manager.js';\nimport type { TokenStore } from './auth/token-store.js';\nimport { Config } from './config.js';\nimport { FetchHttpClient } from './http/fetch-http-client.js';\nimport type { HttpClient } from './http/http-client.js';\nimport { LogRedactor } from './http/log-redactor.js';\nimport { LoggingHttpClient, type DebugSink } from './http/logging-http-client.js';\nimport { Transport } from './http/transport.js';\nimport { UrlBuilder } from './http/url-builder.js';\nimport { DailyFileLogSink } from './log/daily-file-log-sink.js';\nimport { Carts } from './resource/carts.js';\nimport { Categories } from './resource/categories.js';\nimport { Channels } from './resource/channels.js';\nimport { CheckoutEvents } from './resource/checkout-events.js';\nimport { DoctorsNetworks } from './resource/doctors-networks.js';\nimport { Geo } from './resource/geo.js';\nimport { IntakeSubmissions } from './resource/intake-submissions.js';\nimport { LabTests } from './resource/lab-tests.js';\nimport { Medications } from './resource/medications.js';\nimport { Opportunities } from './resource/opportunities.js';\nimport { Patients } from './resource/patients.js';\nimport { Products } from './resource/products.js';\nimport { Sessions } from './resource/sessions.js';\nimport { Shippings } from './resource/shippings.js';\nimport { Teleforms } from './resource/teleforms.js';\nimport { Treatments } from './resource/treatments.js';\nimport { Verification } from './resource/verification.js';\n\n/** Options accepted by {@link AsterMDClient}. */\nexport interface AsterMDClientOptions {\n /** OAuth2 client ID issued from your AsterMD dashboard. */\n clientId: string;\n /**\n * OAuth2 client secret. Load it from an environment variable or a secrets\n * manager - never commit it and never ship it to a browser.\n */\n clientSecret: string;\n /** Bare API hostname, no scheme and no path. Defaults to `api.astermd.com`. */\n baseHost?: string;\n /** Client used for every request. Defaults to {@link FetchHttpClient}. */\n httpClient?: HttpClient;\n /** Where the bearer token is cached. Defaults to {@link InMemoryTokenStore}. */\n tokenStore?: TokenStore;\n /** Per-request timeout in seconds. Must be >= 1. Defaults to 10. */\n timeoutSeconds?: number;\n /**\n * Turn on debug logging of every request and response. Requires `debugFile` or\n * `debugSink`. Defaults to `false`.\n */\n debug?: boolean;\n /**\n * Base path for daily log files, e.g. `/var/log/astermd/sdk.log`. Dated\n * filenames are derived from it. Ignored when `debugSink` is given.\n */\n debugFile?: string;\n /**\n * Receives each formatted entry instead of a file. Supplying this makes\n * retention and delivery yours.\n */\n debugSink?: DebugSink;\n /** IANA time zone for log timestamps and daily filenames. Defaults to `UTC`. */\n debugTimezone?: string;\n /**\n * Mask credentials and PHI in debug output. Defaults to `true`. Setting it\n * `false` writes live bearer tokens and the client secret to your sink - never\n * do that in production.\n */\n debugRedact?: boolean;\n /** Days of log files to keep; `0` keeps everything. Defaults to 7. */\n debugRetentionDays?: number;\n}\n\n/**\n * The SDK's entry point: one client, seventeen resources.\n *\n * Construct it once at application start and share it. It holds the credentials,\n * caches the bearer token, and owns the single transport every resource dispatches\n * through, so creating one per request would throw away the token cache and cost a\n * credential exchange each time.\n *\n * ```ts\n * import { AsterMDClient, Event } from '@astermd-hq/sdk';\n *\n * const client = new AsterMDClient({\n * clientId: process.env.ASTERMD_CLIENT_ID!,\n * clientSecret: process.env.ASTERMD_CLIENT_SECRET!,\n * });\n *\n * const { session } = (\n * await client.sessions().create<{ session: string }>()\n * ).data();\n *\n * await client.intakeSubmissions().create({\n * session,\n * event: Event.PreQualifyingInitiated,\n * teleformId: 'your-teleform-id',\n * data: answers,\n * });\n * ```\n *\n * Credentials are issued from your AsterMD dashboard, which is also where the\n * full API reference lives. This client holds a secret, so it belongs on a\n * server: never construct it in code that reaches a browser.\n *\n * Authentication needs no attention from you. The token is acquired lazily on the\n * first call, reused until it is close to expiring, refreshed automatically if the\n * server rejects it, and the organisation it authorises is encoded inside it -\n * which is why no method here takes an organisation argument.\n *\n * Every accessor is memoized, so `client.sessions()` returns the same instance\n * each time and calling it in a hot path costs nothing.\n */\nexport class AsterMDClient {\n readonly #config: Config;\n readonly #transport: Transport;\n\n #sessions?: Sessions;\n #intakeSubmissions?: IntakeSubmissions;\n #carts?: Carts;\n #checkoutEvents?: CheckoutEvents;\n #teleforms?: Teleforms;\n #patients?: Patients;\n #opportunities?: Opportunities;\n #treatments?: Treatments;\n #doctorsNetworks?: DoctorsNetworks;\n #channels?: Channels;\n #products?: Products;\n #categories?: Categories;\n #labTests?: LabTests;\n #medications?: Medications;\n #shippings?: Shippings;\n #verification?: Verification;\n #geo?: Geo;\n\n /**\n * @param options Credentials, connection settings, and debug configuration.\n * @throws {TypeError} If the credentials are empty, if `baseHost` carries a\n * scheme or path, if `timeoutSeconds` is below 1, or if `debug` is on with\n * neither `debugFile` nor `debugSink`.\n */\n constructor(options: AsterMDClientOptions) {\n const timeoutSeconds = options.timeoutSeconds ?? 10;\n\n this.#config = new Config({\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n baseHost: options.baseHost ?? 'api.astermd.com',\n timeoutSeconds,\n });\n\n let http: HttpClient = options.httpClient ?? new FetchHttpClient(timeoutSeconds);\n\n if (options.debug === true) {\n if (options.debugSink === undefined && options.debugFile === undefined) {\n throw new TypeError('debug mode requires debugFile or debugSink.');\n }\n\n const timeZone = options.debugTimezone ?? 'UTC';\n const sink: DebugSink =\n options.debugSink ??\n ((): DebugSink => {\n const fileSink = new DailyFileLogSink({\n basePath: options.debugFile as string,\n retentionDays: options.debugRetentionDays ?? 7,\n timeZone,\n });\n\n return async (entry: string) => {\n await fileSink.write(entry);\n };\n })();\n\n // Wrap before TokenManager is built, so the credential exchange is logged\n // as well as every resource call.\n http = new LoggingHttpClient({\n inner: http,\n sink,\n timeZone,\n redactor: options.debugRedact === false ? null : new LogRedactor(),\n });\n }\n\n const urlBuilder = new UrlBuilder(this.#config.baseHost());\n\n const tokenManager = new TokenManager({\n config: this.#config,\n httpClient: http,\n urlBuilder,\n store: options.tokenStore ?? new InMemoryTokenStore(),\n });\n\n this.#transport = new Transport({\n httpClient: http,\n urlBuilder,\n tokenProvider: async () => await tokenManager.bearerToken(),\n onUnauthorized: async () => await tokenManager.refresh(),\n });\n }\n\n /**\n * Returns the immutable configuration this client was built with.\n *\n * @returns The configuration, including {@link Config.assetUrl}.\n */\n config(): Config {\n return this.#config;\n }\n\n /**\n * Resolves a relative asset path returned by the API against the AsterMD CDN.\n *\n * A convenience for `client.config().assetUrl(path)`. Treat the result as a\n * fetch URL: download the asset once and serve your own copy rather than\n * hot-linking.\n *\n * @param path Relative asset path as returned by the API.\n * @returns The fully-qualified CDN URL.\n */\n assetUrl(path: string): string {\n return this.#config.assetUrl(path);\n }\n\n /** Sessions - the thread tying one visitor's journey together. */\n sessions(): Sessions {\n return (this.#sessions ??= new Sessions(this.#transport));\n }\n\n /** Intake submissions - pre-qualifying and intake questionnaire answers. */\n intakeSubmissions(): IntakeSubmissions {\n return (this.#intakeSubmissions ??= new IntakeSubmissions(this.#transport));\n }\n\n /** Carts - what the visitor intends to buy. */\n carts(): Carts {\n return (this.#carts ??= new Carts(this.#transport));\n }\n\n /** Checkout events - progress through the checkout funnel. */\n checkoutEvents(): CheckoutEvents {\n return (this.#checkoutEvents ??= new CheckoutEvents(this.#transport));\n }\n\n /** Teleforms - the questionnaire definitions your storefront renders. */\n teleforms(): Teleforms {\n return (this.#teleforms ??= new Teleforms(this.#transport));\n }\n\n /** Patients - the person receiving care. Carries PHI. */\n patients(): Patients {\n return (this.#patients ??= new Patients(this.#transport));\n }\n\n /** Opportunities - a named prospect worth following up. */\n opportunities(): Opportunities {\n return (this.#opportunities ??= new Opportunities(this.#transport));\n }\n\n /** Treatments - the order, once payment has settled. */\n treatments(): Treatments {\n return (this.#treatments ??= new Treatments(this.#transport));\n }\n\n /** Doctors' networks - handing a case to the clinicians who review it. */\n doctorsNetworks(): DoctorsNetworks {\n return (this.#doctorsNetworks ??= new DoctorsNetworks(this.#transport));\n }\n\n /** Channels - the storefront's own server-side configuration. */\n channels(): Channels {\n return (this.#channels ??= new Channels(this.#transport));\n }\n\n /** Products - the read-only catalogue. */\n products(): Products {\n return (this.#products ??= new Products(this.#transport));\n }\n\n /** Categories - how the catalogue is grouped for browsing. */\n categories(): Categories {\n return (this.#categories ??= new Categories(this.#transport));\n }\n\n /** Lab tests - diagnostic panels a product may require. */\n labTests(): LabTests {\n return (this.#labTests ??= new LabTests(this.#transport));\n }\n\n /** Medications - the reference list backing \"what are you currently taking?\". */\n medications(): Medications {\n return (this.#medications ??= new Medications(this.#transport));\n }\n\n /** Shipping options - delivery methods available for an order. */\n shippings(): Shippings {\n return (this.#shippings ??= new Shippings(this.#transport));\n }\n\n /** Verification - address, email, and identity checks. */\n verification(): Verification {\n return (this.#verification ??= new Verification(this.#transport));\n }\n\n /** Geo - where a visitor is, and whether you may serve them. */\n geo(): Geo {\n return (this.#geo ??= new Geo(this.#transport));\n }\n}\n","/**\n * Journey events recorded alongside intake-submission records.\n *\n * Every call to {@link IntakeSubmissions.create} or\n * {@link IntakeSubmissions.update} must carry one of these events to advance the\n * server-side order-flow state machine.\n *\n * Two separate flows exist, each with three events.\n *\n * Pre-qualifying flow - emitted while the prospect answers eligibility\n * questions:\n * - `PreQualifyingInitiated` → first form step (use with `create()`)\n * - `PreQualifyingInProgress` → intermediate steps (use with `update()`)\n * - `PreQualifyingCompleted` → final step / form submitted (use with `update()`)\n *\n * Intake flow - emitted while the prospect fills in the full intake\n * questionnaire:\n * - `IntakeInitiated` → first form step (use with `create()`)\n * - `IntakeInProgress` → intermediate steps (use with `update()`)\n * - `IntakeCompleted` → final step / form submitted (use with `update()`)\n *\n * In both flows the `*Initiated` event always goes to `create()`, the call that\n * creates the server-side record, and the `*InProgress` / `*Completed` events go\n * to subsequent `update()` calls.\n */\nexport const Event = Object.freeze({\n /** The prospect started the pre-qualifying questionnaire. Pass to `create()`. */\n PreQualifyingInitiated: 'pre_qualifying_initiated',\n /** The prospect advanced through an intermediate pre-qualifying step. Pass to `update()`. */\n PreQualifyingInProgress: 'pre_qualifying_inprogress',\n /** The prospect submitted the pre-qualifying form. Pass to `update()`. */\n PreQualifyingCompleted: 'pre_qualifying_completed',\n /** The prospect started the intake questionnaire. Pass to `create()`. */\n IntakeInitiated: 'intake_initiated',\n /** The prospect advanced through an intermediate intake step. Pass to `update()`. */\n IntakeInProgress: 'intake_inprogress',\n /** The prospect submitted the intake form. Pass to `update()`. */\n IntakeCompleted: 'intake_completed',\n} as const);\n\n/** Any journey event wire value. A bare string literal satisfies this type. */\nexport type Event = (typeof Event)[keyof typeof Event];\n","/**\n * The kind of identity check to run through {@link Verification.verifyIdentity}.\n *\n * Each value selects a different check at the configured provider, and each\n * expects a different payload alongside it - a cross-check of supplied details,\n * a date-of-birth confirmation, or a Social Security Number confirmation. The\n * required fields per check are in the API reference in your AsterMD dashboard.\n *\n * These calls carry PII, and `SsnVerify` in particular. The SDK never writes\n * their request bodies to a debug log.\n */\nexport const IdentityCheck = Object.freeze({\n /** Cross-check the supplied identity details against the provider's records. */\n Crosscheck: 'crosscheck',\n /** Confirm the supplied date of birth. */\n DobVerify: 'dob_verify',\n /** Confirm the supplied Social Security Number. */\n SsnVerify: 'ssn_verify',\n} as const);\n\n/** Any identity-check wire value. A bare string literal satisfies this type. */\nexport type IdentityCheck = (typeof IdentityCheck)[keyof typeof IdentityCheck];\n","import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { Token } from './token.js';\nimport type { TokenStore } from './token-store.js';\n\n/**\n * File-backed {@link TokenStore} that persists the JWT across processes.\n *\n * Unlike {@link InMemoryTokenStore}, this writes the token as JSON to disk so it\n * survives a process boundary. That makes it the right choice where each request\n * or invocation gets a fresh process and an in-memory cache would never be hit,\n * as long as those processes share a filesystem.\n *\n * ```ts\n * const client = new AsterMDClient({\n * clientId: process.env.ASTERMD_CLIENT_ID!,\n * clientSecret: process.env.ASTERMD_CLIENT_SECRET!,\n * tokenStore: new FileTokenStore('/var/cache/astermd/token.json'),\n * });\n * ```\n *\n * The parent directory is created if absent. The file is written `0o600` - owner\n * read and write only - so the token is not world-readable on a shared host. It\n * must be writable by the service account and should sit outside any document\n * root. One file per set of API credentials is enough.\n *\n * Writes are atomic: the content goes to a uniquely named temporary file in the\n * same directory and is then renamed over the target, so a concurrent reader\n * sees either the whole previous token or the whole new one. Both reads and\n * writes treat any failure - missing file, bad permissions, a read-only cache\n * directory, truncated or corrupt JSON - as \"no token\" rather than raising,\n * which makes a damaged cache cost one extra credential exchange instead of\n * breaking the request it was only meant to optimise.\n */\nexport class FileTokenStore implements TokenStore {\n readonly #path: string;\n\n /**\n * @param path Path to the JSON file used for token storage. The parent\n * directory is created on first write if it does not exist.\n */\n constructor(path: string) {\n this.#path = path;\n }\n\n /**\n * Reads the cached token from the file.\n *\n * @returns The stored token, or `null` when the file is absent, unreadable, or\n * does not hold a usable token.\n */\n async get(): Promise<Token | null> {\n let contents: string;\n\n try {\n contents = await readFile(this.#path, 'utf8');\n } catch {\n return null;\n }\n\n if (contents === '') {\n return null;\n }\n\n let decoded: unknown;\n\n try {\n decoded = JSON.parse(contents);\n } catch {\n return null;\n }\n\n if (typeof decoded !== 'object' || decoded === null) {\n return null;\n }\n\n const { value, expires_at: expiresAt } = decoded as Record<string, unknown>;\n\n if (typeof value !== 'string' || typeof expiresAt !== 'string') {\n return null;\n }\n\n const parsed = new Date(expiresAt);\n\n if (Number.isNaN(parsed.getTime())) {\n return null;\n }\n\n return new Token(value, parsed);\n }\n\n /**\n * Writes the token to the backing file, replacing any existing content.\n *\n * Persistence is best-effort: a filesystem failure - a read-only or\n * misconfigured cache directory, a permissions problem - is swallowed rather\n * than thrown, matching the PHP SDK's `put()`, which returns early on a\n * `fopen` failure. This method runs on every token acquisition, so failing it\n * would fail the API call it was only meant to optimise; the cost of a\n * degraded cache is an extra credential exchange next time, not a broken\n * request.\n *\n * @param token The freshly acquired token to persist.\n */\n async put(token: Token): Promise<void> {\n const directory = dirname(this.#path);\n\n // Write then rename: rename is atomic, so a concurrent reader never observes\n // a partially written file. Node has no portable equivalent of PHP's flock.\n const temporary = join(directory, `.${randomUUID()}.tmp`);\n\n try {\n await mkdir(directory, { recursive: true, mode: 0o755 });\n\n const contents = JSON.stringify({\n value: token.value(),\n expires_at: token.expiresAt().toISOString(),\n });\n\n await writeFile(temporary, contents, { mode: 0o600 });\n await rename(temporary, this.#path);\n } catch {\n await rm(temporary, { force: true }).catch(() => undefined);\n }\n }\n\n /**\n * Deletes the backing file so the next {@link FileTokenStore.get} returns\n * `null`.\n *\n * A no-op when the file does not exist, including when another process\n * removed it first. Best-effort otherwise: any other filesystem failure (for\n * example a permissions problem) is swallowed rather than thrown, since a\n * cache that fails to clear only costs a stale-token read on the next call,\n * which {@link FileTokenStore.get} already tolerates.\n */\n async clear(): Promise<void> {\n await rm(this.#path, { force: true }).catch(() => undefined);\n }\n}\n","/** One integration's mapping for a variant or a single-price product. */\nexport interface ChannelProductMapping {\n /** The integration's type, from the mapping's `integration.type`. */\n type: unknown;\n /** Every other key of the raw `mapping` object, verbatim. */\n [key: string]: unknown;\n}\n\n/** A purchasable variant of a product, with its price and integration mapping. */\nexport interface ChannelProductVariant {\n id: unknown;\n name: unknown;\n sku: unknown;\n price: unknown;\n intro_price: unknown;\n sale_price: unknown;\n sale_start: unknown;\n sale_end: unknown;\n mapping: ChannelProductMapping | null;\n}\n\n/** Single-purchase pricing for a product that has no variants. */\nexport interface ChannelProductSingle {\n price: unknown;\n intro_price: unknown;\n sale_price: unknown;\n sale_start: unknown;\n sale_end: unknown;\n mapping: ChannelProductMapping | null;\n}\n\n/** A product as the channel offers it. */\nexport interface ChannelProduct {\n id: unknown;\n name: unknown;\n type: unknown;\n sku: unknown;\n status: unknown;\n visibility: unknown;\n image: unknown;\n description_long: unknown;\n description_short: unknown;\n restrict_multiple: unknown;\n min_buy_qty: unknown;\n max_buy_qty: unknown;\n stock: unknown;\n categories: unknown[];\n condition_treated: unknown[];\n teleforms: unknown;\n labtest: unknown[];\n add_ons: ChannelProduct[];\n single: ChannelProductSingle | null;\n variants: ChannelProductVariant[];\n}\n\n/** The channel's payment processor configuration. */\nexport interface ChannelPaymentProcessor {\n id: unknown;\n name: unknown;\n provider: unknown;\n type: unknown;\n config: unknown;\n}\n\n/** The flattened channel-detail envelope. */\nexport interface ChannelDetailView {\n id: unknown;\n name: unknown;\n description: unknown;\n status: unknown;\n type: unknown;\n auto_inc_id: unknown;\n payment_processor: ChannelPaymentProcessor | null;\n products: ChannelProduct[];\n}\n\n/**\n * Read model over the channel-detail envelope.\n *\n * {@link Channels.details} returns everything a storefront needs in one\n * response, which makes it efficient and awkward in equal measure: the payload is\n * deeply nested, uses the server's own field names, and scatters each variant's\n * integration mapping into a separate parallel list keyed by variant id. Rendering\n * straight from it means writing that reassembly into your view layer.\n *\n * This presenter does the reassembly once. It renames `_id` to `id` throughout,\n * joins each variant to its mapping, hoists single-purchase pricing out of its\n * one-element list, flattens reference objects, and recurses through add-ons so\n * they look like ordinary products:\n *\n * ```ts\n * const detail = ChannelDetail.from((await client.channels().details(channelId)).data());\n *\n * for (const product of detail.products) {\n * for (const variant of product.variants) {\n * render(product.name, variant.name, variant.price, variant.mapping);\n * }\n * }\n * ```\n *\n * Field names in the output stay snake_case, matching the PHP SDK's presenter, so\n * a value produced by one can be compared against the other. Anything the server\n * omitted comes back as `null` rather than `undefined`, and an omitted list comes\n * back empty - so a view can read straight through without optional chaining at\n * every level.\n *\n * The class is not instantiable; it exposes only {@link ChannelDetail.from}.\n */\nexport class ChannelDetail {\n private constructor() {\n // Static-only.\n }\n\n /**\n * Flattens a channel-detail payload into a shape a view can render directly.\n *\n * Pass `response.data()` from {@link Channels.details}. Malformed or missing\n * branches degrade to `null` and `[]` rather than raising, because a channel is\n * configuration a storefront needs at boot: failing the page over one absent\n * optional field would be the wrong trade.\n *\n * @param data The raw `data` object from the channel-detail response.\n * @returns The flattened view.\n */\n static from(data: Record<string, unknown>): ChannelDetailView {\n return {\n id: data._id ?? null,\n name: data.name ?? null,\n description: data.description ?? null,\n status: data.status ?? null,\n type: data.type ?? null,\n auto_inc_id: data.auto_inc_id ?? null,\n payment_processor: processor(data.payment_processor),\n products: toList(data.products).map(entry => product(toObject(entry))),\n };\n }\n}\n\nfunction processor(value: unknown): ChannelPaymentProcessor | null {\n if (typeof value !== 'object' || value === null) {\n return null;\n }\n\n const pp = value as Record<string, unknown>;\n\n return {\n id: pp._id ?? null,\n name: pp.name ?? null,\n provider: pp.provider_category ?? null,\n type: pp.type ?? null,\n config: pp.config ?? null,\n };\n}\n\nfunction product(entry: Record<string, unknown>): ChannelProduct {\n // The server sends variant mappings in a parallel list; index them by variant\n // id so each variant can be joined to its own.\n const byVariant = new Map<string, ChannelProductMapping>();\n\n for (const raw of toList(entry.product_mappings)) {\n const pm = toObject(raw);\n const variantId = pm.variant_id;\n\n if (typeof variantId === 'string') {\n byVariant.set(variantId, mapping(pm));\n }\n }\n\n return coreProduct(toObject(entry.product), byVariant, toList(entry.add_ons));\n}\n\nfunction coreProduct(\n p: Record<string, unknown>,\n byVariant: Map<string, ChannelProductMapping>,\n addOns: unknown[],\n): ChannelProduct {\n const id = typeof p._id === 'string' ? p._id : null;\n\n const variants = toList(p.variants).map(raw => {\n const v = toObject(raw);\n const variantId = typeof v._id === 'string' ? v._id : null;\n\n return variant(v, lookup(byVariant, variantId));\n });\n\n const singleRows = toList(p.single);\n const single = singleRows.length === 0 ? null : singlePricing(toObject(singleRows[0]), lookup(byVariant, id));\n\n return {\n id: p._id ?? null,\n name: p.name ?? null,\n type: p.type ?? null,\n sku: p.sku ?? null,\n status: p.status ?? null,\n visibility: p.visibility ?? null,\n image: p.image ?? null,\n description_long: p.description_long ?? null,\n description_short: p.description_short ?? null,\n restrict_multiple: p.restrict_multiple ?? null,\n min_buy_qty: p.min_buy_qty ?? null,\n max_buy_qty: p.max_buy_qty ?? null,\n stock: p.stock ?? null,\n categories: refs(p.categories),\n condition_treated: refs(p.condition_treated),\n teleforms: refs(p.teleforms)[0] ?? null,\n labtest: refs(p.labtest),\n add_ons: addOns.map(addOn => coreProduct(toObject(addOn), new Map(), [])),\n single,\n variants,\n };\n}\n\nfunction variant(v: Record<string, unknown>, productMapping: ChannelProductMapping | null): ChannelProductVariant {\n return {\n id: v._id ?? null,\n name: v.name ?? null,\n sku: v.sku ?? null,\n price: v.default_price ?? null,\n intro_price: v.intro_price ?? null,\n sale_price: v.sale_price ?? null,\n sale_start: v.sale_start ?? null,\n sale_end: v.sale_end ?? null,\n mapping: productMapping,\n };\n}\n\nfunction singlePricing(\n row: Record<string, unknown>,\n productMapping: ChannelProductMapping | null,\n): ChannelProductSingle {\n return {\n price: row.default_price ?? null,\n intro_price: row.intro_price ?? null,\n sale_price: row.sale_price ?? null,\n sale_start: row.sale_start ?? null,\n sale_end: row.sale_end ?? null,\n mapping: productMapping,\n };\n}\n\nfunction mapping(pm: Record<string, unknown>): ChannelProductMapping {\n const integration = toObject(pm.integration);\n const out: ChannelProductMapping = { type: integration.type ?? null };\n\n for (const [key, value] of Object.entries(toObject(pm.mapping))) {\n if (key !== 'type') {\n out[key] = value;\n }\n }\n\n return out;\n}\n\nfunction refs(list: unknown): unknown[] {\n return toList(list).map(item => ref(item));\n}\n\nfunction ref(r: unknown): unknown {\n if (typeof r !== 'object' || r === null) {\n return r;\n }\n\n const raw = r as Record<string, unknown>;\n const out: Record<string, unknown> = { id: raw._id ?? null };\n\n for (const [key, value] of Object.entries(raw)) {\n if (key !== '_id') {\n out[key] = value;\n }\n }\n\n return out;\n}\n\nfunction lookup(map: Map<string, ChannelProductMapping>, key: string | null): ChannelProductMapping | null {\n return key === null ? null : (map.get(key) ?? null);\n}\n\n/** Coerces a value to a list, treating anything else as empty. */\nfunction toList(value: unknown): unknown[] {\n if (Array.isArray(value)) {\n return value;\n }\n\n // A PHP associative array reaches JSON as an object; take its values, as\n // PHP's array_values would.\n return typeof value === 'object' && value !== null ? Object.values(value) : [];\n}\n\n/** Coerces a value to an object, treating anything else as empty. */\nfunction toObject(value: unknown): Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value) ? (value as Record<string, unknown>) : {};\n}\n","import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';\n\nconst ALGORITHM = 'aes-128-cbc';\nconst IV_LENGTH = 16;\n\n/**\n * Encrypts and decrypts URL query-parameter tokens using AES-128-CBC, with a\n * random 16-byte IV prepended to the ciphertext and the whole thing\n * base64url-encoded.\n *\n * Tracking parameters are encrypted so a browser extension cannot read them in\n * transit. The codec is parameter-agnostic: it carries no knowledge of what it\n * is transporting.\n *\n * Decryption is deliberately total. A malformed, truncated, tampered, or\n * wrongly-keyed token yields an empty object rather than raising, so a storefront\n * can fall back to plain parameters instead of failing a page load:\n *\n * ```ts\n * const params = QueryParamCipher.decrypt(\n * request.query._amd ?? '',\n * process.env.ASTERMD_PARAM_KEY!,\n * );\n * // => { aff_id: '123', utm_source: 'news' } (or {} on any failure)\n * ```\n *\n * The AES key is **never** stored in this SDK. Supply it at every call site from\n * your own configuration - an environment variable, a secrets manager - and keep\n * it in step with whichever service mints the tokens. Contact info@astermd.com to\n * obtain or rotate the key for your organisation.\n *\n * **On integrity:** AES-CBC provides confidentiality but not authentication, so a\n * token cannot be proven unmodified. Treat decrypted parameters as untrusted\n * input and validate them before use.\n *\n * **On the wire format and the PHP SDK:** the encrypted payload is a JSON *list*\n * of `{ key, value }` objects, which is what the PHP SDK writes and reads, so a\n * token minted by either SDK is readable by the other. This class's own surface\n * differs from the PHP one, though: it takes and returns a plain object, since\n * that is what a JS caller wants, and converts at the boundary. A duplicate key\n * on the wire resolves to its last occurrence.\n */\nexport class QueryParamCipher {\n private constructor() {\n // Static-only.\n }\n\n /**\n * Decrypts a query-parameter token into tracking parameters.\n *\n * Call this at your storefront's entry point with the raw token from the URL\n * and the key from your own configuration. Any failure along the way - bad\n * encoding, wrong or malformed key, decryption error, non-list payload -\n * returns an empty object so the caller can fall back to plain parameters.\n *\n * @param token The base64url token, `iv(16) || ciphertext`.\n * @param keyHex 32 hex characters - a 16-byte AES-128 key. Supplied by you;\n * the SDK ships no default.\n * @returns The decrypted parameters, or `{}` on any failure.\n */\n static decrypt(token: string, keyHex: string): Record<string, string> {\n if (token === '' || keyHex === '') {\n return {};\n }\n\n const key = decodeKey(keyHex);\n\n if (key === null) {\n return {};\n }\n\n let blob: Buffer;\n\n try {\n blob = Buffer.from(base64UrlToBase64(token), 'base64');\n } catch {\n return {};\n }\n\n if (blob.byteLength <= IV_LENGTH) {\n return {};\n }\n\n let json: string;\n\n try {\n const decipher = createDecipheriv(ALGORITHM, key, blob.subarray(0, IV_LENGTH));\n json = Buffer.concat([decipher.update(blob.subarray(IV_LENGTH)), decipher.final()]).toString('utf8');\n } catch {\n return {};\n }\n\n let decoded: unknown;\n\n try {\n decoded = JSON.parse(json);\n } catch {\n return {};\n }\n\n if (!Array.isArray(decoded)) {\n return {};\n }\n\n // The payload is [{ key, value }, …]; re-shape defensively and drop entries\n // that carry no usable key.\n const out: Record<string, string> = {};\n\n for (const entry of decoded) {\n if (typeof entry !== 'object' || entry === null) {\n continue;\n }\n\n const { key: name, value } = entry as Record<string, unknown>;\n\n if (!isScalar(name)) {\n continue;\n }\n\n out[phpString(name)] = isScalar(value) ? phpString(value) : '';\n }\n\n return out;\n }\n\n /**\n * Encrypts tracking parameters into a token.\n *\n * Provided mainly for round-trip tests and local tooling. A fresh random IV is\n * generated on every call and prepended to the ciphertext before encoding, so\n * the same parameters never produce the same token twice.\n *\n * Unlike {@link QueryParamCipher.decrypt}, this is strict: a bad key raises\n * rather than being swallowed, because a failed encrypt is a programming error\n * and not untrusted input.\n *\n * @param params The parameters to encrypt. Written to the wire as the\n * `[{ key, value }, …]` list the PHP SDK also reads.\n * @param keyHex 32 hex characters - a 16-byte AES-128 key.\n * @returns The base64url token, `iv(16) || ciphertext`.\n * @throws {TypeError} If `keyHex` is not a valid 16-byte hex key.\n */\n static encrypt(params: Record<string, string>, keyHex: string): string {\n const key = decodeKey(keyHex);\n\n if (key === null) {\n throw new TypeError('keyHex must be 32 hex chars (a 16-byte AES-128 key).');\n }\n\n const payload = JSON.stringify(Object.entries(params).map(([name, value]) => ({ key: name, value })));\n\n const iv = randomBytes(IV_LENGTH);\n const cipher = createCipheriv(ALGORITHM, key, iv);\n const ciphertext = Buffer.concat([cipher.update(payload, 'utf8'), cipher.final()]);\n\n return Buffer.concat([iv, ciphertext]).toString('base64url');\n }\n}\n\n/** Decodes a hex key, returning `null` for anything that is not exactly 16 bytes. */\nfunction decodeKey(keyHex: string): Buffer | null {\n if (!/^[0-9a-fA-F]{32}$/.test(keyHex)) {\n return null;\n }\n\n return Buffer.from(keyHex, 'hex');\n}\n\n/**\n * Restores the standard base64 alphabet and padding.\n *\n * `Buffer.from(s, 'base64')` tolerates the base64url alphabet already, but going\n * through the standard form keeps the behaviour identical to the PHP\n * implementation for inputs that mix alphabets.\n */\nfunction base64UrlToBase64(value: string): string {\n const standard = value.replaceAll('-', '+').replaceAll('_', '/');\n const remainder = standard.length % 4;\n\n return remainder === 0 ? standard : standard + '='.repeat(4 - remainder);\n}\n\nfunction isScalar(value: unknown): value is string | number | boolean {\n return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';\n}\n\n/**\n * Renders a scalar the way PHP's `(string)` cast does, for the case that is\n * actually reachable here.\n *\n * `true` becomes `'1'` and `false` becomes the empty string, where JS's\n * `String()` would give `'true'`/`'false'`. This matters because\n * `QueryParamCipher::encrypt()` in the PHP SDK accepts any scalar, so a token\n * minted there can legitimately carry a JSON boolean - and the two SDKs must\n * render it the same way or they disagree on an untampered token.\n *\n * A fractional-valued float is deliberately **not** special-cased. PHP's\n * `(string)` cast on a float uses `%.*G`-style formatting keyed off the\n * `precision` ini setting (14 significant digits by default, but a deployment\n * can change it), which switches to exponential notation on a magnitude *and*\n * significant-digit threshold that does not line up with JS's own\n * number-to-string conversion - e.g. verified against a live PHP install,\n * `(string) 123456789012345.0` is `'1.2345678901234E+14'` (scientific,\n * despite a magnitude under 1e15) while `(string) 0.0001` stays `'0.0001'`\n * but `(string) 0.00001` already becomes `'1.0E-5'`. Reproducing that\n * precisely would mean re-implementing PHP's `precision`-aware float\n * formatter rather than a one-line fix, so it is left unhandled here - an\n * integer-valued float still round-trips correctly (`String(1)` and PHP's\n * `(string) 1.0` both give `'1'`), and only a genuinely fractional value sent\n * from a PHP encrypter can disagree.\n */\nfunction phpString(value: string | number | boolean): string {\n if (typeof value === 'boolean') {\n return value ? '1' : '';\n }\n\n return String(value);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/auth/in-memory-token-store.ts","../src/errors/astermd-error.ts","../src/errors/api-error.ts","../src/errors/authentication-error.ts","../src/errors/not-found-error.ts","../src/errors/rate-limit-error.ts","../src/errors/transport-error.ts","../src/errors/validation-error.ts","../src/response.ts","../src/http/transport.ts","../src/auth/token.ts","../src/auth/token-manager.ts","../src/config.ts","../src/http/fetch-http-client.ts","../src/http/log-redactor.ts","../src/http/curl-formatter.ts","../src/http/logging-http-client.ts","../src/http/url-builder.ts","../src/log/daily-file-log-sink.ts","../src/resource/abstract-resource.ts","../src/resource/carts.ts","../src/resource/categories.ts","../src/resource/channels.ts","../src/enum/checkout-event.ts","../src/resource/checkout-events.ts","../src/resource/coupons.ts","../src/resource/doctors-networks.ts","../src/resource/geo.ts","../src/http/file-upload.ts","../src/resource/intake-submissions.ts","../src/resource/lab-tests.ts","../src/resource/medications.ts","../src/resource/opportunities.ts","../src/resource/patients.ts","../src/resource/products.ts","../src/resource/sessions.ts","../src/resource/shippings.ts","../src/resource/teleforms.ts","../src/resource/treatments.ts","../src/resource/verification.ts","../src/astermd-client.ts","../src/enum/event.ts","../src/enum/identity-check.ts","../src/auth/file-token-store.ts","../src/presenter/channel-detail.ts","../src/support/query-param-cipher.ts"],"names":["retryAfter","fieldErrors","Response","escapeRegExp","basename","extname","stat","readFile","dirname","join","mkdir","rm"],"mappings":"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","file":"index.js","sourcesContent":["import type { Token } from './token.js';\nimport type { TokenStore } from './token-store.js';\n\n/**\n * The default {@link TokenStore}: keeps the token in memory for the life of the\n * process.\n *\n * Right for a long-running server - an Express or Fastify app, a worker - where\n * one credential exchange serves every request until the token expires. Nothing\n * is written anywhere, so nothing can leak to disk.\n *\n * Wrong where each request runs in a fresh process or isolate, since the cache\n * dies with it and every request pays for its own token exchange. Use\n * {@link FileTokenStore} or your own {@link TokenStore} there.\n */\nexport class InMemoryTokenStore implements TokenStore {\n #token: Token | null = null;\n\n /**\n * Returns the token held in memory, or `null` before the first exchange.\n *\n * @returns The cached token, or `null`.\n */\n get(): Promise<Token | null> {\n return Promise.resolve(this.#token);\n }\n\n /**\n * Replaces the token held in memory.\n *\n * @param token The token to cache.\n */\n put(token: Token): Promise<void> {\n this.#token = token;\n\n return Promise.resolve();\n }\n\n /**\n * Drops the token held in memory.\n */\n clear(): Promise<void> {\n this.#token = null;\n\n return Promise.resolve();\n }\n}\n","/**\n * Base class for every error the SDK raises.\n *\n * Catching `AsterMDError` catches everything this SDK can throw and nothing it\n * cannot, which makes it the right thing to guard an integration boundary with.\n * Two branches descend from it: {@link TransportError} for failures where no HTTP\n * response was ever received, and {@link ApiError} for everything the server\n * answered with a non-2xx status.\n *\n * The class is abstract - the SDK never throws it directly.\n */\nexport abstract class AsterMDError extends Error {\n protected constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n // Restores the prototype chain so `instanceof` holds in downlevelled and\n // bundled output, where extending a built-in otherwise loses it.\n Object.setPrototypeOf(this, new.target.prototype);\n this.name = 'AsterMDError';\n\n if (typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(this, new.target);\n }\n }\n}\n","import { AsterMDError } from './astermd-error.js';\n\n/**\n * The API answered with a non-2xx status.\n *\n * This is the base for every status-carrying error and the class the SDK throws\n * for any status it does not map to something more specific - a 400, a 500, a\n * 502 from an upstream hiccup. The decoded response envelope is kept intact on\n * {@link ApiError.envelope} so an integrator can inspect whatever the server\n * chose to send alongside the status.\n *\n * Four subclasses cover the statuses worth branching on explicitly:\n * {@link AuthenticationError} (401), {@link NotFoundError} (404),\n * {@link ValidationError} (422), and {@link RateLimitError} (429).\n */\nexport class ApiError extends AsterMDError {\n readonly #statusCode: number;\n readonly #envelope: Readonly<Record<string, unknown>>;\n\n /**\n * @param message Human-readable message, taken from the envelope's `message`\n * field when the server sent one, otherwise `HTTP {status}`.\n * @param statusCode The HTTP status code the server returned.\n * @param envelope The decoded response body. Copied and frozen, so a later\n * mutation of the caller's object cannot change the error.\n */\n constructor(message: string, statusCode: number, envelope: Record<string, unknown>) {\n super(message);\n this.name = 'ApiError';\n this.#statusCode = statusCode;\n this.#envelope = Object.freeze({ ...envelope });\n }\n\n /**\n * Returns the HTTP status code that caused this error.\n *\n * @returns The status code, e.g. `404` or `500`.\n */\n statusCode(): number {\n return this.#statusCode;\n }\n\n /**\n * Returns the decoded response envelope as the server sent it.\n *\n * Useful for surfacing a server-supplied message or an error code the SDK does\n * not model. The object is frozen; treat it as read-only.\n *\n * @returns The decoded body, or an empty object when the response had none.\n */\n envelope(): Readonly<Record<string, unknown>> {\n return this.#envelope;\n }\n}\n","import { ApiError } from './api-error.js';\n\n/**\n * The API rejected the credentials or the bearer token (HTTP 401).\n *\n * The SDK does not raise this on the first 401. {@link TokenManager} refreshes\n * the token and the request is replayed once; only a second 401 surfaces here.\n * So seeing this error means a fresh token was also refused - check the client ID\n * and secret, and that the credentials are still active in your AsterMD\n * dashboard.\n */\nexport class AuthenticationError extends ApiError {\n constructor(message: string, statusCode: number, envelope: Record<string, unknown>) {\n super(message, statusCode, envelope);\n this.name = 'AuthenticationError';\n }\n}\n","import { ApiError } from './api-error.js';\n\n/**\n * The addressed resource does not exist (HTTP 404).\n *\n * Raised when an id in the request path matches nothing the calling organisation\n * can see. Note that the organisation scope lives inside the JWT, so an id\n * belonging to another organisation is indistinguishable from one that never\n * existed - both arrive here.\n */\nexport class NotFoundError extends ApiError {\n constructor(message: string, statusCode: number, envelope: Record<string, unknown>) {\n super(message, statusCode, envelope);\n this.name = 'NotFoundError';\n }\n}\n","import { ApiError } from './api-error.js';\n\n/**\n * The calling organisation exceeded its request allowance (HTTP 429).\n *\n * When the server sent a `Retry-After` header, its value in seconds is available\n * on {@link RateLimitError.retryAfter} - wait at least that long before trying\n * again. The header is optional, so treat `null` as \"back off on your own\n * schedule\" rather than \"retry immediately\".\n */\nexport class RateLimitError extends ApiError {\n readonly #retryAfter: number | null;\n\n /**\n * @param message Human-readable summary from the envelope.\n * @param statusCode Always 429 in practice.\n * @param envelope The decoded response body.\n * @param retryAfter Seconds to wait, from the `Retry-After` header, or `null`\n * when the header was absent or not a plain integer.\n */\n constructor(message: string, statusCode: number, envelope: Record<string, unknown>, retryAfter: number | null) {\n super(message, statusCode, envelope);\n this.name = 'RateLimitError';\n this.#retryAfter = retryAfter;\n }\n\n /**\n * Returns the server-advised wait in seconds before retrying.\n *\n * @returns Seconds to wait, or `null` when the server did not say.\n */\n retryAfter(): number | null {\n return this.#retryAfter;\n }\n}\n","import { AsterMDError } from './astermd-error.js';\n\n/**\n * A request never reached the API, or its response never came back.\n *\n * Raised for DNS failures, TLS errors, connection resets, and timeouts - every\n * case where the SDK has no HTTP status to reason about. The originating error is\n * preserved on `cause`. There is no `statusCode()` here by design: nothing was\n * returned to have a status.\n *\n * These are usually worth retrying with backoff; an {@link ApiError} usually is\n * not.\n */\nexport class TransportError extends AsterMDError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'TransportError';\n }\n}\n","import { ApiError } from './api-error.js';\n\n/**\n * The request was well-formed but the server refused its contents (HTTP 422).\n *\n * The interesting part is {@link ValidationError.fieldErrors}, which maps each\n * rejected field to the messages the server produced for it - exactly the shape\n * a form needs to render inline errors next to the offending inputs.\n */\nexport class ValidationError extends ApiError {\n readonly #fieldErrors: Readonly<Record<string, string[]>>;\n\n /**\n * @param message Human-readable summary from the envelope.\n * @param statusCode Always 422 in practice.\n * @param envelope The decoded response body.\n * @param fieldErrors Field name → list of messages, extracted from the\n * envelope's `errors` key by {@link Transport}.\n */\n constructor(\n message: string,\n statusCode: number,\n envelope: Record<string, unknown>,\n fieldErrors: Record<string, string[]>,\n ) {\n super(message, statusCode, envelope);\n this.name = 'ValidationError';\n this.#fieldErrors = Object.freeze({ ...fieldErrors });\n }\n\n /**\n * Returns the per-field validation messages the server produced.\n *\n * @returns Field name → messages. Empty when the server sent no `errors` key.\n */\n fieldErrors(): Readonly<Record<string, string[]>> {\n return this.#fieldErrors;\n }\n}\n","/** Options accepted by {@link Response}. */\nexport interface ResponseOptions<T> {\n /** HTTP status code of the response (200–299). */\n statusCode: number;\n /** Contents of the envelope's `data` key. */\n data: T;\n /** Contents of the envelope's `meta` key; may be empty. */\n meta: Record<string, unknown>;\n /** Human-readable `message` string from the envelope. */\n message: string;\n /** Raw response body, before decoding. */\n raw: string;\n}\n\n/**\n * Value object representing a successful API response with its envelope\n * already decoded.\n *\n * Every AsterMD response that returns HTTP 2xx is wrapped in a standard JSON\n * envelope:\n *\n * ```json\n * { \"success\": true, \"message\": \"Record created.\", \"data\": { }, \"meta\": { } }\n * ```\n *\n * {@link Transport} decodes that envelope and builds a `Response` from it.\n * `data()` holds the primary resource payload - `{ session: \"<uuid>\" }` for\n * session creation, for instance - while `meta()` carries pagination cursors and\n * other out-of-band information where the endpoint emits them.\n *\n * Non-2xx responses are never wrapped in this object; they raise\n * {@link ApiError} or one of its subclasses instead.\n *\n * The shape of `data()` varies by endpoint, so it defaults to an untyped record.\n * A caller who knows the shape may narrow it:\n *\n * ```ts\n * const patient = await client.patients().view<Patient>('p-1');\n * ```\n *\n * The instance and its top-level `data`/`meta` objects are frozen, which guards\n * against accidental reassignment or a stray `delete` on this response. That\n * freeze is shallow, though: an object or array nested inside `data()` is the\n * server's payload passed through unchanged, and remains as mutable as any\n * other object you receive from `JSON.parse`. Treat nested values as you would\n * any other deserialised response - copy before mutating if that matters to\n * you - rather than relying on this class to protect them.\n */\nexport class Response<T = Record<string, unknown>> {\n readonly #statusCode: number;\n readonly #data: T;\n readonly #meta: Readonly<Record<string, unknown>>;\n readonly #message: string;\n readonly #raw: string;\n\n /**\n * @param options The decoded envelope fields.\n */\n constructor(options: ResponseOptions<T>) {\n this.#statusCode = options.statusCode;\n this.#data = Object.freeze(Array.isArray(options.data) ? ([...options.data] as T) : { ...options.data });\n this.#meta = Object.freeze({ ...options.meta });\n this.#message = options.message;\n this.#raw = options.raw;\n\n Object.freeze(this);\n }\n\n /**\n * Returns the HTTP status code, always 200–299 for a `Response` instance.\n *\n * @returns The status code, e.g. `200` or `201`.\n */\n statusCode(): number {\n return this.#statusCode;\n }\n\n /**\n * Returns the primary resource payload from the envelope's `data` key.\n *\n * The shape varies by endpoint - see the TSDoc on the resource method you\n * called. Empty when the server returned no data.\n *\n * @returns The decoded `data` object.\n */\n data(): T {\n return this.#data;\n }\n\n /**\n * Returns the envelope's `meta` key, typically pagination information.\n *\n * Present on paginated list endpoints such as `products/list` and\n * `treatments/list`, where it may carry `total`, `per_page`, `current_page`,\n * and `last_page`. Empty for endpoints that emit no `meta` block.\n *\n * @returns The decoded `meta` object.\n */\n meta(): Readonly<Record<string, unknown>> {\n return this.#meta;\n }\n\n /**\n * Returns the human-readable `message` string from the envelope.\n *\n * Usually a short confirmation such as `\"Session created.\"`. Empty when the\n * server omitted the field.\n *\n * @returns The envelope message, possibly empty.\n */\n message(): string {\n return this.#message;\n }\n\n /**\n * Returns the raw response body exactly as received, before decoding.\n *\n * Useful for low-level debugging. Do not log this value for endpoints carrying\n * PHI, such as `patients/health-information`.\n *\n * @returns The raw JSON body.\n */\n raw(): string {\n return this.#raw;\n }\n}\n","import {\n ApiError,\n AsterMDError,\n AuthenticationError,\n NotFoundError,\n RateLimitError,\n TransportError,\n ValidationError,\n} from '../errors/index.js';\nimport { Response as ApiResponse } from '../response.js';\nimport type { FileUpload } from './file-upload.js';\nimport type { HttpClient } from './http-client.js';\nimport type { QueryParams, UrlBuilder } from './url-builder.js';\n\n/** One outbound API call, described declaratively. */\nexport interface SendOptions {\n /** Service segment of the URL: `sales`, `auth`, or `platform`. */\n service: string;\n /** HTTP method in uppercase. */\n method: string;\n /** Path relative to `/v1/{service}`, with `{placeholder}` tokens. */\n path: string;\n /** Placeholder name → value, substituted into `path`. */\n pathParams?: Record<string, string | number>;\n /** Query string parameters. */\n query?: QueryParams;\n /**\n * Request body, serialised as JSON. An empty object is sent as `{}`, which\n * every AsterMD endpoint requires of an object-shaped body. Omit for no body.\n * Mutually exclusive with `file`.\n */\n body?: Record<string, unknown> | null;\n /** Additional headers, e.g. `x-phi-verification-token`. */\n headers?: Record<string, string>;\n /**\n * File to send as a `multipart/form-data` body under the field name `file`.\n * Mutually exclusive with `body`.\n */\n file?: FileUpload | null;\n}\n\n/** Collaborators {@link Transport} needs. */\nexport interface TransportOptions {\n /** The client all outbound calls go through. */\n httpClient: HttpClient;\n /** Builds request URLs. */\n urlBuilder: UrlBuilder;\n /**\n * Returns the current bearer token, or `null` for an unauthenticated request.\n * {@link TokenManager} passes a provider that always returns `null` for its own\n * credential exchange, which is what stops that call recursing.\n */\n tokenProvider: () => Promise<string | null>;\n /**\n * Invoked at most once per `send()` when the server answers 401. Should\n * refresh the token and resolve `true` to request a replay, or `false` to let\n * the 401 surface.\n */\n onUnauthorized: () => Promise<boolean>;\n}\n\n/**\n * The single HTTP chokepoint for every outbound API call.\n *\n * Each resource method delegates to {@link Transport.send}, which owns the whole\n * request lifecycle: URL construction, bearer-token attachment, request\n * building, body serialisation, dispatch through the {@link HttpClient} seam,\n * envelope decoding, and the mapping from HTTP status to error class. Resources\n * never see a `Request`, a `Response`, or a line of JSON - that separation is\n * what keeps eighteen resource classes trivial and testable.\n *\n * The 401-retry protocol is built in. On a 401 the transport calls\n * `onUnauthorized` once, which in the production wiring refreshes the token, and\n * replays the request if that resolves `true`. A second 401 raises\n * {@link AuthenticationError} with no further retry.\n *\n * Status mapping:\n *\n * - 200–299 → {@link Response} with the envelope decoded\n * - 401 → {@link AuthenticationError}\n * - 404 → {@link NotFoundError}\n * - 422 → {@link ValidationError}, with field errors extracted from `errors`\n * - 429 → {@link RateLimitError}, with `Retry-After` parsed when present\n * - anything else → {@link ApiError}\n * - no response at all → {@link TransportError}, from the {@link HttpClient}\n */\nexport class Transport {\n readonly #httpClient: HttpClient;\n readonly #urlBuilder: UrlBuilder;\n readonly #tokenProvider: () => Promise<string | null>;\n readonly #onUnauthorized: () => Promise<boolean>;\n\n /**\n * @param options The collaborators to use for every call.\n */\n constructor(options: TransportOptions) {\n this.#httpClient = options.httpClient;\n this.#urlBuilder = options.urlBuilder;\n this.#tokenProvider = options.tokenProvider;\n this.#onUnauthorized = options.onUnauthorized;\n }\n\n /**\n * Dispatches one API call and returns the decoded success envelope.\n *\n * On a 401 the token is refreshed and the call is replayed once before\n * {@link AuthenticationError} is raised.\n *\n * @param options The call to make.\n * @returns The decoded envelope; `data()` holds the resource payload.\n * @throws {TypeError} If both `body` and `file` are supplied.\n * @throws {AuthenticationError} On a 401 that a token refresh did not recover.\n * @throws {NotFoundError} On a 404.\n * @throws {ValidationError} On a 422; inspect `fieldErrors()`.\n * @throws {RateLimitError} On a 429; inspect `retryAfter()`.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async send<T = Record<string, unknown>>(options: SendOptions): Promise<ApiResponse<T>> {\n if (options.body != null && options.file != null) {\n throw new TypeError('A request carries either a JSON body or a file upload, not both.');\n }\n\n let response = await this.#dispatch(options);\n\n if (response.status === 401 && (await this.#onUnauthorized())) {\n void response.body?.cancel();\n response = await this.#dispatch(options);\n }\n\n return await this.#handle<T>(response);\n }\n\n /**\n * Builds and sends one attempt.\n *\n * A fresh `Request` is constructed on every call rather than reused across the\n * 401 replay. A request body is a single-use stream: sending an already-sent\n * `Request` again transmits an empty body, silently. Keeping the description\n * and materialising the request per attempt is what makes the replay correct.\n */\n async #dispatch(options: SendOptions): Promise<Response> {\n const url = this.#urlBuilder.build(options.service, options.path, options.pathParams ?? {}, options.query ?? {});\n\n const headers = new Headers({ Accept: 'application/json' });\n const token = await this.#tokenProvider();\n\n if (token !== null) {\n headers.set('Authorization', `Bearer ${token}`);\n }\n\n for (const [name, value] of Object.entries(options.headers ?? {})) {\n headers.set(name, value);\n }\n\n let body: string | FormData | null = null;\n\n if (options.body != null) {\n headers.set('Content-Type', 'application/json');\n body = JSON.stringify(options.body);\n } else if (options.file != null) {\n // Every AsterMD endpoint that accepts bytes uses the field name `file`.\n // FormData sets the multipart content type and a fresh boundary itself.\n const form = new FormData();\n form.append(\n 'file',\n new Blob([options.file.contents()], { type: options.file.mimeType() }),\n options.file.fileName(),\n );\n body = form;\n }\n\n try {\n return await this.#httpClient.sendRequest(new Request(url, { method: options.method, headers, body }));\n } catch (error) {\n if (error instanceof AsterMDError) {\n throw error;\n }\n\n const detail = error instanceof Error ? error.message : String(error);\n\n throw new TransportError(`Request to ${url} failed: ${detail}`, {\n cause: error,\n });\n }\n }\n\n async #handle<T>(response: Response): Promise<ApiResponse<T>> {\n let raw: string;\n\n try {\n raw = await response.text();\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n\n throw new TransportError(`Reading the response body failed: ${detail}`, {\n cause: error,\n });\n }\n\n const envelope = decode(raw);\n const status = response.status;\n\n if (status >= 200 && status < 300) {\n return new ApiResponse<T>({\n statusCode: status,\n data: objectField(envelope, 'data') as T,\n meta: objectField(envelope, 'meta'),\n message: typeof envelope.message === 'string' ? envelope.message : '',\n raw,\n });\n }\n\n const message = typeof envelope.message === 'string' ? envelope.message : `HTTP ${status}`;\n\n switch (status) {\n case 401:\n throw new AuthenticationError(message, status, envelope);\n case 404:\n throw new NotFoundError(message, status, envelope);\n case 422:\n throw new ValidationError(message, status, envelope, fieldErrors(envelope));\n case 429:\n throw new RateLimitError(message, status, envelope, retryAfter(response));\n default:\n throw new ApiError(message, status, envelope);\n }\n }\n}\n\n/** Decodes a response body, treating anything unparseable as an empty envelope. */\nfunction decode(raw: string): Record<string, unknown> {\n if (raw === '') {\n return {};\n }\n\n try {\n const decoded: unknown = JSON.parse(raw);\n\n return typeof decoded === 'object' && decoded !== null ? (decoded as Record<string, unknown>) : {};\n } catch {\n return {};\n }\n}\n\n/**\n * Reads one envelope field that should hold structured data.\n *\n * A list payload is preserved as a list - several endpoints return `data` as an\n * array - and anything scalar or missing becomes an empty object.\n */\nfunction objectField(envelope: Record<string, unknown>, key: string): Record<string, unknown> {\n const value = envelope[key];\n\n return typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : {};\n}\n\n/** Normalises the envelope's `errors` key into field name → messages. */\nfunction fieldErrors(envelope: Record<string, unknown>): Record<string, string[]> {\n const errors = envelope.errors;\n\n // An array-shaped `errors` payload has only integer keys, which PHP skips.\n // Surfacing them as field names \"0\", \"1\" would be worse than reporting none.\n if (typeof errors !== 'object' || errors === null || Array.isArray(errors)) {\n return {};\n }\n\n const out: Record<string, string[]> = {};\n\n for (const [field, messages] of Object.entries(errors as Record<string, unknown>)) {\n if (Array.isArray(messages)) {\n out[field] = messages.filter((message): message is string => typeof message === 'string');\n } else if (typeof messages === 'string') {\n out[field] = [messages];\n } else if (typeof messages === 'object' && messages !== null) {\n // PHP's is_array also accepts a JSON object here (an associative array\n // on the PHP side); take its values the same way the array branch does.\n out[field] = Object.values(messages).filter((message): message is string => typeof message === 'string');\n }\n }\n\n return out;\n}\n\n/** Parses `Retry-After`, accepting only a plain integer count of seconds. */\nfunction retryAfter(response: Response): number | null {\n const header = response.headers.get('Retry-After');\n\n return header !== null && /^\\d+$/.test(header) ? Number(header) : null;\n}\n","/**\n * Immutable value object holding a JWT bearer token and its server-stated\n * expiry.\n *\n * {@link TokenManager} builds one after a successful credential exchange and\n * hands it to a {@link TokenStore}. Before each request the manager calls\n * {@link Token.isExpired} to decide whether the cached token can be reused or a\n * fresh one must be acquired.\n *\n * The organisation the token authorises is encoded inside the JWT itself, which\n * is why no SDK method takes an organisation argument.\n */\nexport class Token {\n readonly #value: string;\n readonly #expiresAtMs: number;\n\n /**\n * @param value The raw JWT string, to be sent as `Authorization: Bearer <value>`.\n * @param expiresAt Expiry as declared by the token response's\n * `access_token_expiry` field. Copied, so a later mutation of\n * the caller's `Date` cannot change the token.\n */\n constructor(value: string, expiresAt: Date) {\n this.#value = value;\n this.#expiresAtMs = expiresAt.getTime();\n\n Object.freeze(this);\n }\n\n /**\n * Returns the raw JWT string.\n *\n * @returns The JWT, ready for use as a bearer credential.\n */\n value(): string {\n return this.#value;\n }\n\n /**\n * Returns the server-stated expiry.\n *\n * A fresh `Date` each call, so callers cannot mutate the token's state. Note\n * that the SDK treats the token as expired before this moment - see\n * {@link Token.isExpired}.\n *\n * @returns The expiry as declared in the token response.\n */\n expiresAt(): Date {\n return new Date(this.#expiresAtMs);\n }\n\n /**\n * Reports whether this token should be considered expired and replaced.\n *\n * The pre-buffer avoids a race where the token is valid when checked but\n * expires in transit before the server processes the request. The default 30\n * seconds means the SDK only trusts a token with at least half a minute of\n * validity left. Pass a custom value only when you know something specific\n * about clock skew in your environment.\n *\n * @param preBufferSeconds Seconds to subtract from the stated expiry when\n * computing the effective cutoff.\n * @returns `true` when the token is at or past its effective expiry.\n */\n isExpired(preBufferSeconds = 30): boolean {\n return Date.now() >= this.#expiresAtMs - preBufferSeconds * 1000;\n }\n}\n","import type { Config } from '../config.js';\nimport { AuthenticationError } from '../errors/index.js';\nimport type { HttpClient } from '../http/http-client.js';\nimport { Transport } from '../http/transport.js';\nimport type { UrlBuilder } from '../http/url-builder.js';\nimport { InMemoryTokenStore } from './in-memory-token-store.js';\nimport { Token } from './token.js';\nimport type { TokenStore } from './token-store.js';\n\n/** Collaborators {@link TokenManager} needs. */\nexport interface TokenManagerOptions {\n /** Credentials and connection settings. */\n config: Config;\n /** Client used for the credential exchange. */\n httpClient: HttpClient;\n /** URL builder, shared with the main transport. */\n urlBuilder: UrlBuilder;\n /** Storage for the cached JWT. Defaults to {@link InMemoryTokenStore}. */\n store?: TokenStore;\n /**\n * Seconds before the stated expiry at which the token is treated as expired,\n * guarding against clock skew. Defaults to 30.\n */\n expiryPreBufferSeconds?: number;\n}\n\n/**\n * Owns the bearer-token lifecycle: lazy acquisition, caching, expiry, refresh.\n *\n * This is the only place in the SDK that exchanges credentials for a JWT. It\n * dispatches that exchange through its own {@link Transport}, built with a token\n * provider that always returns `null` and an unauthorized handler that always\n * returns `false`. That is not a detail - it is what makes recursion impossible.\n * A manager that used the main transport would ask it for a bearer token, which\n * would ask the manager, which would ask the transport, forever.\n *\n * On each {@link TokenManager.bearerToken} call the store is checked for a token\n * that is present and not within `expiryPreBufferSeconds` of expiry. Otherwise\n * `POST /v1/auth/api-credentials/token` is called and the result stored.\n *\n * Acquisition is de-duplicated: concurrent callers arriving while an exchange is\n * in flight all await that same exchange rather than starting their own. On a\n * long-running Node server the first burst of traffic after startup would\n * otherwise fire one credential exchange per concurrent request.\n *\n * The organisation is encoded in the JWT the server issues, which is why no SDK\n * method accepts an organisation argument.\n */\nexport class TokenManager {\n readonly #config: Config;\n readonly #store: TokenStore;\n readonly #expiryPreBufferSeconds: number;\n readonly #tokenTransport: Transport;\n\n #inFlight: Promise<Token> | null = null;\n\n /**\n * @param options The collaborators and tuning to use.\n */\n constructor(options: TokenManagerOptions) {\n this.#config = options.config;\n this.#store = options.store ?? new InMemoryTokenStore();\n this.#expiryPreBufferSeconds = options.expiryPreBufferSeconds ?? 30;\n\n // Token acquisition runs through a transport with no token provider and no\n // 401 retry. Recursion here would be a bug, so it is made unrepresentable.\n this.#tokenTransport = new Transport({\n httpClient: options.httpClient,\n urlBuilder: options.urlBuilder,\n tokenProvider: () => Promise.resolve(null),\n onUnauthorized: () => Promise.resolve(false),\n });\n }\n\n /**\n * Returns a valid bearer token, acquiring one if the cache cannot serve it.\n *\n * A cached, unexpired token is returned with no HTTP call. Otherwise the\n * credential exchange runs and its result is stored before being returned. Two\n * concurrent calls that both miss the cache share a single exchange.\n *\n * @returns The raw JWT, ready for `Authorization: Bearer <value>`.\n * @throws {AuthenticationError} If the credentials are refused, or if the token\n * response omits `access_token` or `access_token_expiry`.\n * @throws {TransportError} If the exchange never completed.\n */\n async bearerToken(): Promise<string> {\n const cached = await this.#store.get();\n\n if (cached !== null && !cached.isExpired(this.#expiryPreBufferSeconds)) {\n return cached.value();\n }\n\n return (await this.#acquire()).value();\n }\n\n /**\n * Discards the cached token, acquires a fresh one, and reports `true`.\n *\n * Always resolves `true` on success so it can be wired straight to\n * {@link Transport}'s `onUnauthorized` hook, where `true` means \"token\n * refreshed, replay the request once\". A second 401 on that replay raises\n * {@link AuthenticationError} without calling this again.\n *\n * @returns `true`.\n * @throws {AuthenticationError} If the fresh acquisition fails.\n * @throws {TransportError} If the exchange never completed.\n */\n async refresh(): Promise<boolean> {\n await this.#store.clear();\n await this.#acquire();\n\n return true;\n }\n\n /**\n * Runs the credential exchange, sharing one in-flight attempt across callers.\n *\n * The in-flight promise is cleared in a `finally`, so a failed exchange is\n * never cached - the next caller gets a fresh attempt rather than the stored\n * rejection.\n */\n async #acquire(): Promise<Token> {\n this.#inFlight ??= this.#exchange().finally(() => {\n this.#inFlight = null;\n });\n\n return await this.#inFlight;\n }\n\n async #exchange(): Promise<Token> {\n const response = await this.#tokenTransport.send({\n service: 'auth',\n method: 'POST',\n path: '/api-credentials/token',\n body: {\n client_id: this.#config.clientId(),\n client_secret: this.#config.clientSecret(),\n },\n });\n\n const data = response.data();\n const value = data.access_token;\n const expiry = data.access_token_expiry;\n\n if (typeof value !== 'string' || typeof expiry !== 'string') {\n throw new AuthenticationError(\n 'Token response missing access_token / access_token_expiry.',\n response.statusCode(),\n { data },\n );\n }\n\n const expiresAt = new Date(expiry);\n\n if (Number.isNaN(expiresAt.getTime())) {\n throw new AuthenticationError(\n `Token response carried an unparseable access_token_expiry: ${expiry}`,\n response.statusCode(),\n { data },\n );\n }\n\n const token = new Token(value, expiresAt);\n await this.#store.put(token);\n\n return token;\n }\n}\n","/** Base URL of the AsterMD asset CDN, including the trailing slash. */\nconst ASSET_BASE_URL = 'https://cdn.astermd.com/';\n\n/** Default bare API hostname. */\nconst DEFAULT_BASE_HOST = 'api.astermd.com';\n\n/** Options accepted by {@link Config}. */\nexport interface ConfigOptions {\n /** OAuth2 client ID issued by AsterMD. Must be non-empty. */\n clientId: string;\n /** OAuth2 client secret corresponding to `clientId`. Must be non-empty. */\n clientSecret: string;\n /** Bare API hostname without scheme or path. Defaults to `api.astermd.com`. */\n baseHost?: string;\n /** Per-request timeout in seconds. Must be >= 1. Defaults to 10. */\n timeoutSeconds?: number;\n}\n\n/**\n * Immutable value object holding SDK configuration.\n *\n * Stores the OAuth2 credentials and connection settings every outbound request\n * needs. It is constructed once inside {@link AsterMDClient} and never mutated.\n * Validation happens eagerly in the constructor, so a misconfigured client fails\n * at boot rather than at its first network call.\n *\n * `baseHost` is a bare hostname. The SDK always prepends `https://` and appends\n * `/v1/{service}/{path}` itself - never pass a full URL.\n */\nexport class Config {\n readonly #clientId: string;\n readonly #clientSecret: string;\n readonly #baseHost: string;\n readonly #timeoutSeconds: number;\n\n /**\n * @param options Credentials and connection settings.\n * @throws {TypeError} If `clientId` or `clientSecret` is empty, if `baseHost`\n * contains a scheme or a path, or if `timeoutSeconds` is below 1.\n */\n constructor(options: ConfigOptions) {\n const { clientId, clientSecret, baseHost = DEFAULT_BASE_HOST, timeoutSeconds = 10 } = options;\n\n if (clientId === '' || clientSecret === '') {\n throw new TypeError('clientId and clientSecret must be non-empty.');\n }\n\n if (baseHost.includes('://') || baseHost.includes('/')) {\n throw new TypeError(`baseHost must be a bare host (no scheme, no path). Got: ${baseHost}`);\n }\n\n if (timeoutSeconds < 1) {\n throw new TypeError('timeoutSeconds must be >= 1.');\n }\n\n this.#clientId = clientId;\n this.#clientSecret = clientSecret;\n this.#baseHost = baseHost;\n this.#timeoutSeconds = timeoutSeconds;\n\n Object.freeze(this);\n }\n\n /**\n * Returns the OAuth2 client ID used to acquire JWT bearer tokens.\n *\n * @returns The client ID, guaranteed non-empty.\n */\n clientId(): string {\n return this.#clientId;\n }\n\n /**\n * Returns the OAuth2 client secret used to acquire JWT bearer tokens.\n *\n * @returns The client secret, guaranteed non-empty.\n */\n clientSecret(): string {\n return this.#clientSecret;\n }\n\n /**\n * Returns the bare API hostname used to build request URLs.\n *\n * Never contains a scheme or a path. {@link UrlBuilder} prepends `https://`\n * and appends `/v1/{service}/{path}` at request time.\n *\n * @returns The hostname, e.g. `api.astermd.com`.\n */\n baseHost(): string {\n return this.#baseHost;\n }\n\n /**\n * Returns the per-request HTTP timeout in seconds.\n *\n * Applied by {@link FetchHttpClient} as an `AbortSignal` deadline covering the\n * whole request. A custom {@link HttpClient} may use it at its own discretion.\n *\n * @returns The timeout in seconds, always >= 1.\n */\n timeoutSeconds(): number {\n return this.#timeoutSeconds;\n }\n\n /**\n * Builds the fully-qualified CDN URL for a media asset path returned by the API.\n *\n * API responses carry relative asset paths such as\n * `{org}/{channel}/products/{file}.png`. Pass one here to resolve it.\n *\n * Treat the result as a *fetch* URL, not a *serve* URL: download the asset\n * once, store it on your own filesystem or CDN, and serve your copy to end\n * users. Hot-linking the AsterMD CDN from a storefront is not supported and is\n * subject to rate limiting.\n *\n * @param path Relative asset path as returned by the API; a leading slash is\n * optional and is trimmed.\n * @returns The fully-qualified URL.\n */\n assetUrl(path: string): string {\n return ASSET_BASE_URL + path.replace(/^\\/+/, '');\n }\n}\n","import { TransportError } from '../errors/index.js';\nimport type { HttpClient } from './http-client.js';\n\n/**\n * The default {@link HttpClient}: the platform `fetch` plus a timeout.\n *\n * This is what {@link AsterMDClient} uses unless you inject something else. It\n * adds exactly two things to `fetch`. First, a deadline: every request carries an\n * `AbortSignal.timeout()` built from the configured `timeoutSeconds`, covering\n * connection and response together. Second, uniform failures: a DNS error, a\n * connection reset, a TLS failure, or a timeout all surface as\n * {@link TransportError} with the original error preserved on `cause`, so\n * integrators can catch one type instead of guessing at platform error shapes.\n *\n * It holds no connection state, so one instance is safely shared across\n * concurrent requests.\n */\nexport class FetchHttpClient implements HttpClient {\n readonly #timeoutSeconds: number;\n readonly #fetchImpl: typeof fetch;\n\n /**\n * @param timeoutSeconds Per-request deadline in seconds. Must be >= 1.\n * @param fetchImpl The `fetch` implementation to call. Defaults to the global\n * one; override it to supply an instrumented or stubbed\n * `fetch` without implementing the whole interface.\n * @throws {TypeError} If `timeoutSeconds` is below 1.\n */\n constructor(timeoutSeconds = 10, fetchImpl: typeof fetch = globalThis.fetch) {\n if (timeoutSeconds < 1) {\n throw new TypeError('timeoutSeconds must be >= 1.');\n }\n\n this.#timeoutSeconds = timeoutSeconds;\n this.#fetchImpl = fetchImpl;\n }\n\n /**\n * Sends the request through `fetch` under the configured deadline.\n *\n * @param request The request to send.\n * @returns The response as received, whatever its status.\n * @throws {TransportError} If the request aborts on the deadline or fails at\n * the network layer.\n */\n async sendRequest(request: Request): Promise<Response> {\n try {\n return await this.#fetchImpl(request, {\n signal: AbortSignal.timeout(this.#timeoutSeconds * 1000),\n });\n } catch (error) {\n if (error instanceof Error && error.name === 'TimeoutError') {\n throw new TransportError(`Request to ${request.url} timed out after ${this.#timeoutSeconds}s.`, {\n cause: error,\n });\n }\n\n const detail = error instanceof Error ? error.message : String(error);\n\n throw new TransportError(`Request to ${request.url} failed: ${detail}`, { cause: error });\n }\n }\n}\n","/** Header names whose values are always replaced, compared case-insensitively. */\nconst SENSITIVE_HEADERS = new Set(['authorization', 'x-phi-verification-token']);\n\n/** JSON field names whose values are always replaced, compared case-insensitively. */\nconst SENSITIVE_FIELDS = ['client_secret', 'access_token', 'refresh_token'];\n\n/** Path fragments that mark an endpoint as carrying PII or PHI. */\nconst PHI_PATH_FRAGMENTS = ['/patients', '/extensions/identity-verify'];\n\n/** Path fragments that mark a request body as raw file bytes. */\nconst BINARY_UPLOAD_FRAGMENTS = ['/intake-submissions/upload-file/', '/upload-file-multipart/part/'];\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Strips credentials and protected health information out of debug log entries.\n *\n * Debug logging renders every outbound request as a copy-pasteable curl command\n * (see {@link CurlFormatter}). Left untouched, those entries would contain the\n * bearer JWT, the client secret exchanged for it, and - on patient endpoints -\n * PHI. This class is the single place that decides what must never reach a log\n * sink, and {@link CurlFormatter} and {@link LoggingHttpClient} both apply it\n * whenever redaction is enabled.\n *\n * Redaction is **on by default**: constructing {@link AsterMDClient} with\n * `debug: true` produces safe logs. Pass `debugRedact: false` to get verbatim\n * output when you genuinely need to replay a request by hand - never in\n * production.\n *\n * Values replaced with {@link LogRedactor.PLACEHOLDER}:\n *\n * - the `Authorization` header value (the bearer JWT), scheme preserved;\n * - the `x-phi-verification-token` header value;\n * - the `client_secret` field of a token-exchange request body;\n * - the `access_token` and `refresh_token` fields of any response body.\n *\n * Bodies are dropped whole for `/patients/*` and `/extensions/identity-verify`,\n * which carry PII and PHI - a Social Security Number in the identity case - that\n * must not appear in a log under any circumstances.\n *\n * File-upload bodies are dropped too, for two reasons: the bytes of a scanned ID\n * or a consultation video are PHI, and rendering megabytes of binary into a curl\n * command would make the log unusable anyway. That covers the single-shot upload\n * endpoint and the multipart `part` endpoint. The multipart `initiate`, `finish`,\n * and `abort` bodies are small JSON control messages carrying no file contents,\n * so they are logged in full where they are useful.\n *\n * Redaction covers headers and bodies only - **the request URL is logged\n * verbatim**. Endpoints that take their input as a query parameter therefore log\n * that input, so entries for `extensions/email-verify`,\n * `extensions/geo-info`, `extensions/geo-blocklist`, and the address endpoints\n * contain the email address or IP address that was checked. Keep that in mind\n * when deciding where debug logs are written and how long they are kept.\n */\nexport class LogRedactor {\n /** Replacement text substituted for every redacted value. */\n static readonly PLACEHOLDER = '[REDACTED]';\n\n /** Marker written in place of a body dropped wholesale. */\n static readonly PHI_PLACEHOLDER = '[REDACTED - PHI endpoint]';\n\n /** Marker written in place of the raw file bytes of an upload. */\n static readonly UPLOAD_PLACEHOLDER = '[REDACTED - binary upload]';\n\n /**\n * Returns the loggable form of one request header value.\n *\n * A sensitive header keeps its name, so the log still shows the header was\n * sent, but its value is replaced. For `Authorization` the scheme survives -\n * `Bearer [REDACTED]` - because knowing the scheme helps when debugging and\n * carries no secret.\n *\n * @param name The header name as it appears on the request.\n * @param value The raw header value.\n * @returns The value to write to the log.\n */\n headerValue(name: string, value: string): string {\n if (!SENSITIVE_HEADERS.has(name.toLowerCase())) {\n return value;\n }\n\n const scheme = /^(\\S+)\\s+\\S/.exec(value);\n\n return scheme === null ? LogRedactor.PLACEHOLDER : `${scheme[1]!} ${LogRedactor.PLACEHOLDER}`;\n }\n\n /**\n * Returns the loggable form of a request or response body.\n *\n * A body belonging to a PHI-carrying path, and the raw bytes sent to an upload\n * endpoint, are replaced entirely. Every other body has its sensitive JSON\n * fields rewritten in place, leaving the surrounding structure intact so the\n * entry stays readable and replayable apart from the redacted values.\n *\n * @param body The raw body as it would otherwise be logged.\n * @param path The request path, used to detect PHI and upload endpoints.\n * @returns The body to write to the log.\n */\n body(body: string, path: string): string {\n if (body === '') {\n return body;\n }\n\n if (this.isPhiPath(path)) {\n return LogRedactor.PHI_PLACEHOLDER;\n }\n\n if (this.isBinaryUploadPath(path)) {\n return LogRedactor.UPLOAD_PLACEHOLDER;\n }\n\n return this.#redactFields(body);\n }\n\n /**\n * Reports whether a path carries PII or PHI and must have its body dropped.\n *\n * @param path The request path, with or without the `/v1/{service}` prefix.\n * @returns `true` for a patient endpoint or the identity-verification endpoint.\n */\n isPhiPath(path: string): boolean {\n const lower = path.toLowerCase();\n\n return PHI_PATH_FRAGMENTS.some(fragment => lower.includes(fragment));\n }\n\n /**\n * Reports whether a path carries raw file bytes and must have its body dropped.\n *\n * True for the single-shot intake-submission upload endpoint and the multipart\n * `part` endpoint. The multipart `initiate`, `finish`, and `abort` endpoints\n * carry small JSON control messages, so they are excluded and their bodies stay\n * in the log where they help.\n *\n * @param path The request path, with or without the `/v1/{service}` prefix.\n * @returns `true` when the body is file contents rather than JSON.\n */\n isBinaryUploadPath(path: string): boolean {\n const lower = path.toLowerCase();\n\n return BINARY_UPLOAD_FRAGMENTS.some(fragment => lower.includes(fragment));\n }\n\n /**\n * Extracts the path from a request, for use with the methods above.\n *\n * @param request The request to inspect.\n * @returns The URL path, without the query string.\n */\n pathOf(request: Request): string {\n return new URL(request.url).pathname;\n }\n\n /**\n * Replaces the value of every sensitive JSON field found in the given text.\n *\n * Operates on the serialised form rather than decoding, so it works for\n * pretty-printed and compact JSON alike and never reorders or reformats a body\n * that is only partly JSON.\n */\n #redactFields(body: string): string {\n let out = body;\n\n for (const field of SENSITIVE_FIELDS) {\n const pattern = new RegExp(`(\"${escapeRegExp(field)}\"\\\\s*:\\\\s*)\"(?:[^\"\\\\\\\\]|\\\\\\\\.)*\"`, 'gi');\n out = out.replace(pattern, `$1\"${LogRedactor.PLACEHOLDER}\"`);\n }\n\n return out;\n }\n}\n","import type { LogRedactor } from './log-redactor.js';\n\n/**\n * Converts a request into a copy-pasteable multi-line curl command.\n *\n * Used only by {@link LoggingHttpClient}, to produce log entries a developer can\n * replay in a terminal or import into an API client. The class has no instance\n * state and exposes a single static method.\n *\n * With a {@link LogRedactor} the rendered command has its credentials and PHI\n * stripped, so the entry stays structurally faithful while being safe to write to\n * disk or ship to an aggregator. Without one it is rendered verbatim and will\n * contain the live bearer token.\n *\n * Two details differ from the PHP formatter, both forced by the platform. The\n * method is asynchronous, because reading a request body is. And header names\n * appear lowercased, because `Headers` normalises them - curl does not care, and\n * the redactor compares names case-insensitively.\n */\nexport class CurlFormatter {\n /**\n * Renders the given request as a multi-line curl invocation.\n *\n * Each header becomes its own `--header '...'` line. A body is appended as\n * `--data '...'` with single quotes shell-escaped through the `'\\''` idiom.\n * With no body, the trailing backslash continuation is stripped from the last\n * line so the command pastes cleanly.\n *\n * The request's body is read from a clone, so the request itself stays unread\n * and can still be sent. Reading the original would leave the real request with\n * an empty body.\n *\n * @param request The request to format.\n * @param redactor Applied to header values and the body before rendering. Pass\n * `null` or omit it to render verbatim.\n * @returns The multi-line curl command.\n */\n static async format(request: Request, redactor: LogRedactor | null = null): Promise<string> {\n const lines = [`curl --location --request ${request.method} '${request.url}' \\\\`];\n const headerLines: string[] = [];\n\n for (const [name, value] of request.headers.entries()) {\n const shown = redactor?.headerValue(name, value) ?? value;\n headerLines.push(` --header '${name}: ${shown}' \\\\`);\n }\n\n let body = await readBody(request);\n\n if (redactor !== null) {\n body = redactor.body(body, redactor.pathOf(request));\n }\n\n if (body !== '') {\n lines.push(...headerLines);\n // Escape single quotes for the shell via the '\\'' idiom.\n lines.push(` --data '${body.replaceAll(\"'\", String.raw`'\\''`)}'`);\n } else {\n headerLines.forEach((line, index) => {\n lines.push(index === headerLines.length - 1 ? line.replace(/ \\\\$/, '') : line);\n });\n\n if (headerLines.length === 0) {\n lines[0] = lines[0]!.replace(/ \\\\$/, '');\n }\n }\n\n return lines.join('\\n');\n }\n}\n\n/**\n * Reads a request body without consuming the request.\n *\n * A multipart body is reported as its placeholder length rather than decoded: the\n * redactor drops upload bodies by path anyway, and materialising the bytes only to\n * throw them away wastes memory on exactly the requests that are largest.\n */\nasync function readBody(request: Request): Promise<string> {\n if (request.body === null) {\n return '';\n }\n\n const contentType = request.headers.get('content-type') ?? '';\n\n if (contentType.startsWith('multipart/form-data')) {\n return '[multipart/form-data]';\n }\n\n return await request.clone().text();\n}\n","import { CurlFormatter } from './curl-formatter.js';\nimport type { HttpClient } from './http-client.js';\nimport { LogRedactor } from './log-redactor.js';\n\n/**\n * Destination for debug log entries.\n *\n * Called once per request with a fully formatted entry - timestamp, curl\n * command, and response. Return a promise to have the SDK await the write. Any\n * error it throws is swallowed: a failing log must never fail an API call.\n */\nexport type DebugSink = (entry: string) => void | Promise<void>;\n\n/** Collaborators {@link LoggingHttpClient} needs. */\nexport interface LoggingHttpClientOptions {\n /** The client to delegate the actual request to. */\n inner: HttpClient;\n /** Where formatted entries are written. */\n sink: DebugSink;\n /** IANA time zone for entry timestamps. Defaults to `UTC`. */\n timeZone?: string;\n /**\n * Strips credentials and PHI from each entry. Defaults to a\n * {@link LogRedactor}; pass `null` to log verbatim, including live bearer\n * tokens - never in production.\n */\n redactor?: LogRedactor | null;\n}\n\n/**\n * {@link HttpClient} decorator that logs every request and its response as a\n * curl command.\n *\n * Wraps any client, forwards each call to it unchanged, and captures the request\n * and response for debug logging. Each entry carries a timestamp, the request\n * rendered as a replayable curl command by {@link CurlFormatter}, and the HTTP\n * status plus response body.\n *\n * {@link AsterMDClient} applies this decorator *before* handing the client to\n * {@link TokenManager}, so one decorator covers the credential exchange as well\n * as every resource call. That also makes it the only place in the SDK that could\n * leak a credential, which is why a {@link LogRedactor} is attached by default.\n *\n * The response body is read from a clone and the caller receives a fresh response\n * built from the bytes already read, so `Transport` can still decode the\n * envelope. A sink that throws is ignored, and an asynchronous sink is awaited\n * before the response is returned.\n */\nexport class LoggingHttpClient implements HttpClient {\n readonly #inner: HttpClient;\n readonly #sink: DebugSink;\n readonly #redactor: LogRedactor | null;\n readonly #formatter: Intl.DateTimeFormat;\n\n /**\n * @param options The client to wrap and where to log.\n */\n constructor(options: LoggingHttpClientOptions) {\n this.#inner = options.inner;\n this.#sink = options.sink;\n this.#redactor = options.redactor === undefined ? new LogRedactor() : options.redactor;\n this.#formatter = new Intl.DateTimeFormat('en-CA', {\n timeZone: options.timeZone ?? 'UTC',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n hourCycle: 'h23',\n timeZoneName: 'short',\n });\n }\n\n /**\n * Forwards the request to the inner client, then logs the exchange.\n *\n * @param request The request to send and log.\n * @returns The response, with its body still readable.\n * @throws The inner client's error, rethrown after being logged.\n */\n async sendRequest(request: Request): Promise<Response> {\n const timestamp = this.#timestamp();\n const curl = await CurlFormatter.format(request, this.#redactor);\n\n let response: Response;\n\n try {\n response = await this.#inner.sendRequest(request);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n await this.#write(`[${timestamp}]\\n${curl}\\n\\n# Transport error: ${detail}\\n\\n`);\n\n throw error;\n }\n\n // Read the body once, then hand the caller a response rebuilt from those\n // bytes. A response body is a single-use stream, so logging it would\n // otherwise leave Transport with nothing to decode.\n const raw = await response.clone().text();\n const shown = this.#redactor?.body(raw, this.#redactor.pathOf(request)) ?? raw;\n\n await this.#write(`[${timestamp}]\\n${curl}\\n\\n# Response: HTTP ${response.status}\\n${shown}\\n\\n`);\n\n // 204/205/304 must be constructed with a null body; an empty string is\n // still a body and the Response constructor rejects it.\n const nullBody = response.status === 204 || response.status === 205 || response.status === 304;\n\n return new Response(nullBody ? null : raw, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n }\n\n async #write(entry: string): Promise<void> {\n try {\n await this.#sink(entry);\n } catch {\n // Losing a debug entry is never worth failing the request that produced it.\n }\n }\n\n /**\n * Formats the current instant as `YYYY-MM-DD HH:MM:SS.ffffff ZONE`.\n *\n * PHP logs microseconds; `Date` only resolves to milliseconds, so the\n * millisecond value is zero-padded to six digits to keep the column width and\n * the shape of the PHP entries.\n */\n #timestamp(): string {\n const now = new Date();\n const parts = new Map(this.#formatter.formatToParts(now).map(({ type, value }) => [type, value]));\n const micros = String(now.getMilliseconds()).padStart(3, '0').padEnd(6, '0');\n\n return (\n `${parts.get('year')!}-${parts.get('month')!}-${parts.get('day')!} ` +\n `${parts.get('hour')!}:${parts.get('minute')!}:${parts.get('second')!}.${micros} ` +\n `${parts.get('timeZoneName')!}`\n );\n }\n}\n","/** Query string parameters accepted by {@link UrlBuilder.build}. */\nexport type QueryParams = Record<string, string | number | boolean>;\n\n/**\n * Percent-encodes a value the way PHP's `rawurlencode()` does.\n *\n * `encodeURIComponent()` leaves `!`, `'`, `(`, `)`, and `*` unescaped, which\n * `rawurlencode()` escapes. The difference matters because URLs built by the PHP\n * SDK and by this one are compared against the same server and against each\n * other in tests.\n */\nfunction rawUrlEncode(value: string): string {\n return encodeURIComponent(value).replace(\n /[!'()*]/g,\n character => `%${character.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n}\n\n/**\n * Serialises query parameters the way PHP's `http_build_query()` does.\n *\n * That means RFC 1738 encoding: a space becomes `+`, and `~` becomes `%7E`.\n * `URLSearchParams` agrees on the space but not on the tilde, so this is written\n * out rather than delegated. Booleans serialise as `1` and `0`, matching PHP's\n * coercion of `true` and `false`.\n */\nfunction buildQuery(query: QueryParams): string {\n return Object.entries(query)\n .map(([key, value]) => {\n const raw = typeof value === 'boolean' ? (value ? '1' : '0') : String(value);\n\n return `${urlEncode(key)}=${urlEncode(raw)}`;\n })\n .join('&');\n}\n\nfunction urlEncode(value: string): string {\n return rawUrlEncode(value).replace(/%20/g, '+').replace(/~/g, '%7E');\n}\n\n/**\n * Builds fully-qualified HTTPS request URLs following the `/v1/{service}/{path}`\n * convention.\n *\n * Every AsterMD endpoint lives under `https://{host}/v1/{service}/{path}`, where\n * `{service}` is `auth`, `sales`, or `platform`. This class is the one place that\n * convention is encoded, so resources never construct URLs and the SDK can never\n * be handed a full URL by a consumer.\n *\n * Path placeholders such as `{id}` are substituted and percent-encoded; query\n * parameters are appended with PHP-compatible encoding so a URL built here is\n * byte-identical to the one the PHP SDK would build for the same call.\n */\nexport class UrlBuilder {\n readonly #baseHost: string;\n\n /**\n * @param baseHost Bare hostname to prepend, e.g. `api.astermd.com`.\n */\n constructor(baseHost: string) {\n this.#baseHost = baseHost;\n }\n\n /**\n * Constructs the fully-qualified URL for a service and path.\n *\n * Placeholder tokens in `path` - `{id}`, `{session}`, `{form_json_identifier}`\n * - are replaced with the matching values from `pathParams`, percent-encoded.\n * A placeholder with no matching entry is left untouched rather than guessed\n * at, which surfaces the programming error in the request URL. Non-empty\n * `query` entries are serialised and appended after `?`.\n *\n * @param service Service segment, e.g. `sales`, `auth`, `platform`.\n * @param path Path relative to `/v1/{service}`, with optional `{placeholder}`\n * tokens.\n * @param pathParams Placeholder name → replacement value.\n * @param query Query string parameters.\n * @returns The fully-qualified URL.\n */\n build(\n service: string,\n path: string,\n pathParams: Record<string, string | number> = {},\n query: QueryParams = {},\n ): string {\n let resolved = path;\n\n for (const [key, value] of Object.entries(pathParams)) {\n resolved = resolved.split(`{${key}}`).join(rawUrlEncode(String(value)));\n }\n\n const url = `https://${this.#baseHost}/v1/${service}${resolved}`;\n const queryString = Object.keys(query).length > 0 ? buildQuery(query) : '';\n\n return queryString === '' ? url : `${url}?${queryString}`;\n }\n}\n","import { appendFile, mkdir, readdir, rm } from 'node:fs/promises';\nimport { basename, dirname, extname, join } from 'node:path';\n\n/** Options accepted by {@link DailyFileLogSink}. */\nexport interface DailyFileLogSinkOptions {\n /**\n * Undated path the daily filenames are derived from, e.g.\n * `/var/log/astermd/sdk.log`. A path with no extension gets `.log`.\n */\n basePath: string;\n /**\n * Days of logs to keep. A file is removed once its date is more than this many\n * days before today. Use `0` to keep every file forever. Defaults to 7.\n */\n retentionDays?: number;\n /**\n * IANA time zone deciding which calendar day an entry belongs to. Pass the same\n * zone used for log timestamps so filenames and their contents agree. Defaults\n * to `UTC`.\n */\n timeZone?: string;\n}\n\n/**\n * Debug-log sink that writes one file per calendar day and prunes old files.\n *\n * The debug logger emits one entry per HTTP request. Appending those to a single\n * file grows without bound, which is why this sink dates each file and deletes\n * the ones that have aged out - the same arrangement as conventional daily log\n * rotation, but self-contained, so no external rotation tooling is needed.\n *\n * You give it a *base path* and it derives dated filenames from it, keeping the\n * directory, stem, and extension you chose:\n *\n * ```\n * basePath: /var/log/astermd/sdk.log\n *\n * /var/log/astermd/sdk-2026-09-08.log ← today, appended to\n * /var/log/astermd/sdk-2026-09-07.log\n * /var/log/astermd/sdk-2026-09-01.log ← deleted once it is over 7 days old\n * ```\n *\n * {@link AsterMDClient} builds one for you when you pass `debugFile`; construct\n * it directly only if you want to reuse one sink across several clients.\n *\n * Pruning reads the date out of each filename rather than the filesystem's\n * modification time, so a file that was merely touched is still removed on\n * schedule and unrelated files sharing the directory are never matched. It runs\n * at most once per instance, on the first write, which keeps it off the hot path.\n *\n * Writes are serialized through an internal promise chain, so concurrent requests\n * cannot interleave halves of two entries. Every I/O failure is swallowed: losing\n * a debug entry is never worth failing the request that produced it.\n *\n * Supplying your own `debugSink` bypasses this class entirely, and retention then\n * becomes that system's responsibility.\n */\nexport class DailyFileLogSink {\n readonly #directory: string;\n readonly #stem: string;\n readonly #extension: string;\n readonly #retentionDays: number;\n readonly #dateFormatter: Intl.DateTimeFormat;\n\n #pruned = false;\n #tail: Promise<void> = Promise.resolve();\n\n /**\n * @param options Base path, retention window, and time zone.\n * @throws {TypeError} If `basePath` is empty or carries no file name, or if\n * `retentionDays` is negative.\n */\n constructor(options: DailyFileLogSinkOptions) {\n const { basePath, retentionDays = 7, timeZone = 'UTC' } = options;\n\n if (basePath.trim() === '') {\n throw new TypeError('basePath must be a non-empty file path.');\n }\n\n if (retentionDays < 0) {\n throw new TypeError('retentionDays must be >= 0 (0 disables pruning).');\n }\n\n const extension = extname(basePath);\n const stem = basename(basePath, extension);\n\n if (stem === '') {\n throw new TypeError(`basePath must include a file name. Got: ${basePath}`);\n }\n\n this.#directory = dirname(basePath);\n this.#stem = stem;\n this.#extension = extension === '' ? '.log' : extension;\n this.#retentionDays = retentionDays;\n this.#dateFormatter = new Intl.DateTimeFormat('en-CA', {\n timeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n });\n }\n\n /**\n * Appends one entry to today's log file, pruning aged-out files on the first\n * call.\n *\n * Resolves once the entry is on disk. Writes are chained, so entries land whole\n * and in call order even under concurrency. Never rejects: an I/O failure is\n * swallowed rather than propagated into the API call being logged.\n *\n * @param entry The formatted log entry to append.\n */\n async write(entry: string): Promise<void> {\n this.#tail = this.#tail.then(() => this.#append(entry)).catch(() => undefined);\n\n await this.#tail;\n }\n\n /**\n * Returns the absolute path entries are currently written to.\n *\n * Useful in tests and for a \"where are my logs\" diagnostic. The path changes at\n * midnight in the configured time zone.\n *\n * @returns The dated file path, e.g. `/var/log/astermd/sdk-2026-09-08.log`.\n */\n currentFile(): string {\n return join(this.#directory, `${this.#stem}-${this.#today()}${this.#extension}`);\n }\n\n async #append(entry: string): Promise<void> {\n try {\n // Owner-only: with debugRedact: false a raw entry can carry a live\n // bearer token, so the directory and file must not be readable by every\n // user on the host, the same rule FileTokenStore already follows.\n await mkdir(this.#directory, { recursive: true, mode: 0o700 });\n await appendFile(this.currentFile(), entry, { mode: 0o600 });\n\n if (!this.#pruned) {\n this.#pruned = true;\n await this.#prune();\n }\n } catch {\n // Losing a debug entry is never worth failing the request that produced it.\n }\n }\n\n /**\n * Deletes dated log files older than the retention window.\n *\n * Only files matching this sink's own `{stem}-YYYY-MM-DD{ext}` pattern are\n * considered, and the date comes from the filename rather than filesystem\n * metadata. A file that cannot be deleted is skipped silently.\n */\n async #prune(): Promise<void> {\n if (this.#retentionDays === 0) {\n return;\n }\n\n const cutoff = this.#cutoff();\n const pattern = new RegExp(`^${escapeRegExp(this.#stem)}-(\\\\d{4}-\\\\d{2}-\\\\d{2})${escapeRegExp(this.#extension)}$`);\n\n let names: string[];\n\n try {\n names = await readdir(this.#directory);\n } catch {\n return;\n }\n\n for (const name of names) {\n const matched = pattern.exec(name);\n\n // ISO dates sort lexicographically, so a string comparison is a date\n // comparison. A file exactly `retentionDays` old is kept, matching PHP.\n if (matched !== null && matched[1]! < cutoff) {\n try {\n await rm(join(this.#directory, name), { force: true });\n } catch {\n // A file we cannot delete is not worth failing over; PHP's @unlink\n // skips silently too. Try the next one.\n }\n }\n }\n }\n\n #today(): string {\n return this.#dateFormatter.format(new Date());\n }\n\n /** The oldest date still kept, as `YYYY-MM-DD`. */\n #cutoff(): string {\n const today = new Date(`${this.#today()}T00:00:00.000Z`);\n today.setUTCDate(today.getUTCDate() - this.#retentionDays);\n\n return today.toISOString().slice(0, 10);\n }\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n","import type { Transport } from '../http/transport.js';\n\n/**\n * Base class for every SDK resource; holds the shared {@link Transport}.\n *\n * Resource classes extend this and call `this.transport.send()` to dispatch every\n * outbound request. A resource never builds a request, touches a header, or\n * decodes JSON - all of that belongs to `Transport` alone. That is what keeps\n * eighteen resource classes small enough to read at a glance and testable\n * without a network.\n */\nexport abstract class AbstractResource {\n /** The shared transport used for all outbound API calls. */\n protected readonly transport: Transport;\n\n /**\n * @param transport The transport every call from this resource goes through.\n */\n protected constructor(transport: Transport) {\n this.transport = transport;\n }\n}\n","import type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/** Options for {@link Carts.create} and {@link Carts.update}. */\nexport interface CartWriteOptions {\n /** The session the cart belongs to. */\n session: string;\n /**\n * The cart's full contents. Each entry requires `product_id`, `name`, and\n * `qty`, plus an optional `variant_id` when the product has variants. This is\n * the complete list, not a delta - see the API reference in your AsterMD\n * dashboard for the fields.\n */\n items: Record<string, unknown>[];\n}\n\n/**\n * Carts - what the visitor intends to buy, recorded against their session.\n *\n * The cart is a server-side record of intent, kept so abandonment can be measured\n * and so the eventual order can be reconciled against what was selected. It is\n * not a pricing engine and not a checkout: money is handled by your own payment\n * flow, and the resulting order arrives through\n * {@link Treatments.create} or {@link Treatments.sync}.\n *\n * Both methods take the cart's *whole* contents rather than a delta. Mirror your\n * storefront's cart state into `update()` whenever it changes, and the\n * server-side record stays in step with what the visitor is actually looking at.\n */\nexport class Carts extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Creates the cart for a session.\n *\n * Call this the first time the visitor puts something in their cart. The\n * session must already exist. If a cart is already recorded for the session,\n * use {@link Carts.update} instead.\n *\n * @param options The session and the cart's contents.\n * @returns The created cart.\n * @throws {ValidationError} If the session or an item is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async create<T = Record<string, unknown>>(options: CartWriteOptions): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/carts/create',\n body: { session: options.session, items: options.items },\n });\n }\n\n /**\n * Replaces the contents of a session's cart.\n *\n * The item list is authoritative: whatever you send becomes the cart. Send the\n * full list on every change rather than only what moved, and pass an empty list\n * to record that the visitor emptied their cart.\n *\n * @param options The session and the cart's new contents.\n * @returns The updated cart.\n * @throws {NotFoundError} If no cart exists for the session.\n * @throws {ValidationError} If an item is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async update<T = Record<string, unknown>>(options: CartWriteOptions): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PUT',\n path: '/carts/update/{session}',\n pathParams: { session: options.session },\n body: { items: options.items },\n });\n }\n}\n","import type { QueryParams } from '../http/url-builder.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Categories - how the catalogue is grouped for browsing.\n *\n * A category is a merchandising grouping a storefront renders as navigation: a\n * condition treated, a product family, a landing-page collection. Products\n * reference the categories they belong to, so fetching the category list is\n * usually the first call a catalogue page makes.\n *\n * Read-only, and configured in your AsterMD dashboard.\n */\nexport class Categories extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Lists categories, filtered and paginated.\n *\n * Pagination details come back in `meta()`. The accepted filters are in the\n * API reference in your AsterMD dashboard.\n *\n * @param query Filters and pagination parameters.\n * @returns The matching categories, with pagination in `meta()`.\n * @throws {ValidationError} If a filter is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async list<T = Record<string, unknown>>(query: QueryParams = {}): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/categories/list',\n query,\n });\n }\n\n /**\n * Fetches one category.\n *\n * @param id The category's id.\n * @returns The category record.\n * @throws {NotFoundError} If the category does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/categories/view/{id}',\n pathParams: { id },\n });\n }\n}\n","import type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Channels - the storefront's own configuration, as the server holds it.\n *\n * A channel is one selling surface: a brand, a site, a campaign landing page.\n * Which products it may sell, which payment processor it uses, and which\n * questionnaires those products require are all channel-level configuration set\n * in your AsterMD dashboard. Your storefront reads that configuration rather than\n * duplicating it, so changing what a brand sells does not need a deployment.\n *\n * Three views, in increasing depth. {@link Channels.view} returns the channel\n * itself. {@link Channels.assignedProducts} returns what it may sell.\n * {@link Channels.details} returns the whole tree - channel, payment processor,\n * products, variants, prices, and per-integration mappings - in a single call,\n * which is what a storefront usually wants at boot.\n *\n * Channel configuration changes rarely. Fetch it rather than embedding a copy,\n * but caching the result for minutes is reasonable.\n */\nexport class Channels extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Fetches a channel's own configuration.\n *\n * The channel record itself - name, type, status - without its product\n * catalogue.\n *\n * @param id The channel's id.\n * @returns The channel record.\n * @throws {NotFoundError} If the channel does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/channels/view/{id}',\n pathParams: { id },\n });\n }\n\n /**\n * Fetches the products assigned to a channel.\n *\n * What this selling surface is permitted to sell. Use it to build a catalogue\n * page without pulling the full detail tree.\n *\n * @param id The channel's id.\n * @returns The assigned products.\n * @throws {NotFoundError} If the channel does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async assignedProducts<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/channels/assigned-products/{id}',\n pathParams: { id },\n });\n }\n\n /**\n * Fetches a channel's complete configuration tree.\n *\n * Channel, payment processor, products, variants, prices, and per-integration\n * mappings in one response - everything a storefront needs to render its\n * catalogue without further calls. The envelope is deeply nested and carries\n * server-side field names; see the API reference in your AsterMD dashboard for\n * its shape. Pass `data()` through {@link ChannelDetail.from} to flatten it.\n *\n * @param id The channel's id.\n * @returns The full configuration tree.\n * @throws {NotFoundError} If the channel does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async details<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/channels/detail/{id}',\n pathParams: { id },\n });\n }\n}\n","/**\n * Checkout-funnel events recorded against a session.\n *\n * {@link CheckoutEvents.create} always records `CheckoutVisited` - it is the\n * call that opens the funnel record. Every later state is reported through\n * {@link CheckoutEvents.update} with one of the remaining values, which lets the\n * server reconstruct where a prospect abandoned checkout and which upsells they\n * were shown.\n */\nexport const CheckoutEvent = Object.freeze({\n /** The prospect reached the checkout page. Recorded implicitly by `create()`. */\n CheckoutVisited: 'checkout_visited',\n /** An upsell was presented to the prospect. */\n UpsellOffered: 'upsell_offered',\n /** The prospect accepted a presented upsell. */\n UpsellAccepted: 'upsell_accepted',\n /** The prospect declined a presented upsell. */\n UpsellDeclined: 'upsell_declined',\n /** The order was placed successfully. */\n OrderPlaced: 'order_placed',\n /** The order was declined, typically by the payment processor. */\n OrderDeclined: 'order_declined',\n} as const);\n\n/** Any checkout-funnel event wire value. A bare string literal satisfies this type. */\nexport type CheckoutEvent = (typeof CheckoutEvent)[keyof typeof CheckoutEvent];\n","import { CheckoutEvent } from '../enum/checkout-event.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/** Currency recorded when the caller does not specify one. */\nconst DEFAULT_CURRENCY = 'USD';\n\n/** Options for {@link CheckoutEvents.update}. */\nexport interface CheckoutEventUpdateOptions {\n /** The session whose checkout record is being advanced. */\n session: string;\n /** The funnel state now reached. */\n event: CheckoutEvent;\n /**\n * Additional context for this event - which upsell was shown, the order total,\n * a decline reason. Forwarded unmodified except that it can never override\n * `event`.\n */\n data?: Record<string, unknown>;\n}\n\n/**\n * Checkout events - the visitor's progress through the checkout funnel.\n *\n * Where {@link Sessions} records that a visit happened and {@link Carts} records\n * what was selected, this resource records what happened at the till: the\n * checkout page opening, each upsell offered and accepted or declined, and\n * finally whether the order was placed or declined. Together those events are\n * what makes checkout abandonment measurable.\n *\n * {@link CheckoutEvents.create} opens the record and always registers\n * `checkout_visited`; every later state goes through\n * {@link CheckoutEvents.update} with the matching {@link CheckoutEvent}. Neither\n * call can have its event overridden by the extra payload, so the funnel state\n * the server records is always the one the SDK's caller chose explicitly.\n */\nexport class CheckoutEvents extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Opens the checkout record for a session, registering `checkout_visited`.\n *\n * Call this once, when the visitor reaches the checkout page. The event is\n * fixed - this is the call that starts the funnel record - and `currency`\n * defaults to `USD` when you do not supply one, since the server requires a\n * currency to interpret any amounts in the payload.\n *\n * @param session The session reaching checkout.\n * @param data Additional context, e.g. `cart_total`, `items_count`,\n * `currency`. Cannot override `session` or `event`.\n * @returns The created checkout-event record.\n * @throws {ValidationError} If the session or payload is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async create<T = Record<string, unknown>>(\n session: string,\n data: Record<string, unknown> = {},\n ): Promise<ApiResponse<T>> {\n // Data first, fixed keys last: PHP's `+` keeps the left operand on a\n // conflict, so the payload must not be able to rewrite session or event.\n const body: Record<string, unknown> = {\n ...data,\n session,\n event: CheckoutEvent.CheckoutVisited,\n };\n\n if (body.currency === undefined || body.currency === '') {\n body.currency = DEFAULT_CURRENCY;\n }\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/checkout-events/create',\n body,\n });\n }\n\n /**\n * Advances a session's checkout record to a new funnel state.\n *\n * Send one call per state the visitor reaches: an upsell offered, then accepted\n * or declined, then the order placed or declined. The payload carries whatever\n * context that state needs, but it can never override `event` - the state\n * recorded is always the one you passed explicitly. For an `OrderPlaced` event,\n * `data` may include an optional `payment` object describing the settled\n * payment method: `type` (`paypal` | `apple_pay` | `gpay` | `credit_card` |\n * `pre_paid`), `pre_auth` (boolean), `pre_auth_qa` and `pre_auth_amount`\n * (optional), and an optional `card` object (`type` - `amex` | `visa` |\n * `mastercard` | `discover` | `diners_club` | `jcb`, optional `bin`, required\n * `exp`).\n *\n * @param options The session, the new state, and any context for it.\n * @returns The updated checkout-event record.\n * @throws {NotFoundError} If no checkout record exists for the session.\n * @throws {ValidationError} If the event or payload is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async update<T = Record<string, unknown>>(options: CheckoutEventUpdateOptions): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PUT',\n path: '/checkout-events/update/{session_id}',\n pathParams: { session_id: options.session },\n body: { ...(options.data ?? {}), event: options.event },\n });\n }\n}\n","import type { Response as ApiResponse } from '../response.js';\nimport type { Transport } from '../http/transport.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * One cart line for {@link Coupons.validate}: either a subscription plan, or a\n * product with its variant - never both. Never an amount; AsterMD prices the\n * line itself.\n */\nexport type CouponCartItem =\n | {\n /** The subscription plan the line is sold on. Priced at the plan's first payment. */\n plan_id: string;\n /** Units of the line, an integer from 1 to 999. */\n quantity: number;\n }\n | {\n /** The AsterMD product. */\n product_id: string;\n /** The product's variant; the product id again for a product without variants. */\n variant_id: string;\n /** Units of the line, an integer from 1 to 999. */\n quantity: number;\n };\n\n/** Options for {@link Coupons.validate}. */\nexport interface CouponValidateOptions {\n /** The code the shopper typed. Not case sensitive. */\n code: string;\n /** The cart, main item first. Between 1 and 6 lines. */\n items: CouponCartItem[];\n /**\n * The AsterMD patient id of the shopper, when already a patient. Sent only\n * when provided. When several of `patientId`, `email` and `opportunityId` are\n * given, the server uses them in that order.\n */\n patientId?: string;\n /** The shopper's email; used when `patientId` is not sent. Sent only when provided. */\n email?: string;\n /**\n * The shopper's opportunity id; used when neither `patientId` nor `email` is\n * sent. Sent only when provided.\n */\n opportunityId?: string;\n}\n\n/**\n * Coupons - checking a code the shopper typed against their cart.\n *\n * Use this resource just before creating the payment: it tells the storefront\n * whether a coupon code can be used on the current cart, and what it takes off.\n * AsterMD prices the cart itself from its catalogue, so callers send product,\n * variant or plan identifiers and quantities - never amounts.\n *\n * The call applies to a channel whose payment provider is a storefront gateway.\n * Nothing is counted against the coupon by a check; redemption is recorded later,\n * when the order is synced.\n *\n * ```ts\n * const { discount_cents, amount_cents } = (\n * await client.coupons().validate<{ discount_cents: number; amount_cents: number }>({\n * code: 'WELCOME20',\n * items: [{ plan_id: planId, quantity: 1 }],\n * email: 'jane.doe@example.com',\n * })\n * ).data();\n * ```\n */\nexport class Coupons extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Checks a coupon code against a cart without redeeming it.\n *\n * Call this when the shopper applies a code at checkout, before the payment is\n * created. The server validates the coupon's status, validity window, patient,\n * products, budget, scope and redemption limits, and prices the cart. The check\n * has no side effects. Inspect `data()` on the response for the coupon terms,\n * the price before and after the discount, and the discounted lines. A shopper\n * who is not a patient yet only fits a coupon open to anyone.\n *\n * @param options The code, the cart, and optionally who the shopper is\n * (`patientId`, `email` or `opportunityId`). Each optional identifier is sent\n * only when provided.\n * @returns The coupon check; `data()` holds `coupon_id`, `name`,\n * `discount_type`, `value`, `tiers`, `default_scope`, `gross_amount_cents`,\n * `discount_cents`, `amount_cents`, `currency` and the discounted `items`.\n * @throws {NotFoundError} If no coupon exists with this code.\n * @throws {ValidationError} If the coupon cannot be used on this cart, or the\n * cart cannot be priced.\n * @throws {AuthenticationError} If the credentials are refused.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async validate<T = Record<string, unknown>>(options: CouponValidateOptions): Promise<ApiResponse<T>> {\n const body: Record<string, unknown> = {\n code: options.code,\n items: options.items,\n };\n\n if (options.patientId !== undefined) {\n body.patient_id = options.patientId;\n }\n\n if (options.email !== undefined) {\n body.email = options.email;\n }\n\n if (options.opportunityId !== undefined) {\n body.opportunity_id = options.opportunityId;\n }\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/coupons/validate',\n body,\n });\n }\n}\n","import type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Doctors' networks - handing a case to the clinicians who will review it.\n *\n * A doctors' network is the panel of licensed clinicians configured for your\n * organisation. Syncing pushes a case to that panel so a clinician can pick it\n * up: it is the bridge between the commercial half of the order flow and the\n * clinical one.\n *\n * The single method here is unusual in the SDK for validating before it sends. A\n * sync with neither an opportunity to attach to nor enough detail to identify the\n * person would be accepted by the transport and rejected by the server, having\n * cost a round trip to learn something the SDK can see locally. So it checks\n * first, and raises `TypeError` rather than making the call.\n *\n * ```ts\n * await client.doctorsNetworks().sync({ opportunity_id: opportunityId });\n *\n * // or, without an opportunity yet:\n * await client.doctorsNetworks().sync({\n * user_info: { first_name: 'Jane', email: 'jane@example.test' },\n * });\n * ```\n */\nexport class DoctorsNetworks extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Pushes a case to the configured doctors' network.\n *\n * The payload must identify who the case is about, one of two ways: an\n * `opportunity_id` for a prospect already recorded, or a `user_info` object\n * carrying at least `first_name` and `email` for one who is not. Everything\n * else is forwarded unmodified.\n *\n * An `emit_opportunity` key is removed before sending - that flag is the\n * server's to set, not a caller's. Your object is not modified; the SDK copies\n * it first.\n *\n * @param payload The case to push. Requires either a non-empty\n * `opportunity_id` string, or a `user_info` object with non-empty\n * `first_name` and `email` strings.\n * @returns The sync result.\n * @throws {TypeError} If the payload identifies neither an opportunity nor a\n * person.\n * @throws {ValidationError} If the server rejects a field.\n * @throws {NotFoundError} If the referenced opportunity does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async sync<T = Record<string, unknown>>(payload: Record<string, unknown>): Promise<ApiResponse<T>> {\n // Copy before deleting: PHP's unset acts on a by-value array copy, but in JS\n // this object belongs to the caller.\n const body = { ...payload };\n delete body.emit_opportunity;\n\n validate(body);\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/doctors-networks/sync',\n body,\n });\n }\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value !== '';\n}\n\n/**\n * Requires the payload to identify the case's subject one of the two accepted\n * ways.\n *\n * @throws {TypeError} If it identifies neither.\n */\nfunction validate(payload: Record<string, unknown>): void {\n const hasOpportunity = isNonEmptyString(payload.opportunity_id);\n\n const userInfo = payload.user_info;\n const hasMinimalUserInfo =\n typeof userInfo === 'object' &&\n userInfo !== null &&\n isNonEmptyString((userInfo as Record<string, unknown>).first_name) &&\n isNonEmptyString((userInfo as Record<string, unknown>).email);\n\n if (!hasOpportunity && !hasMinimalUserInfo) {\n throw new TypeError('doctors-networks/sync requires either opportunity_id or user_info with first_name + email.');\n }\n}\n","import type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Geo - where a visitor is, and whether you are allowed to serve them.\n *\n * Telehealth is licensed jurisdiction by jurisdiction, so location is not a\n * personalisation detail here - it decides whether an order flow may start at\n * all. These two endpoints answer the two different questions that follow from\n * that.\n *\n * {@link Geo.info} resolves an IP address to a location, which is what you want\n * for pre-filling a state selector or routing a visitor to the right regional\n * content. {@link Geo.blocklist} answers the narrower question of whether this\n * address is barred - a jurisdiction you are not licensed in, a known proxy or\n * anonymiser. Check the blocklist at the top of the funnel: turning a visitor\n * away on the landing page is far cheaper than doing it after they have completed\n * an intake questionnaire.\n *\n * Pass the *visitor's* address, not your server's. Behind a proxy or load\n * balancer that means reading the forwarded-for header your infrastructure sets,\n * with the same care you would apply anywhere else - that header is\n * caller-supplied unless your edge overwrites it.\n *\n * Both take their input as a query parameter, and redaction never covers the URL,\n * so a debug entry for these calls contains the IP address that was checked.\n */\nexport class Geo extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Resolves an IP address to a geographic location.\n *\n * Returns the configured provider's answer, normalised. Treat it as a strong\n * hint rather than proof - a VPN or a mobile carrier's routing can place a\n * visitor a long way from where they are. For a licensing decision, prefer\n * {@link Geo.blocklist} and the state the visitor declares.\n *\n * @param ip The visitor's IP address, v4 or v6.\n * @returns The resolved location.\n * @throws {ValidationError} If the address is malformed.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async info<T = Record<string, unknown>>(ip: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'platform',\n method: 'GET',\n path: '/extensions/geo-info',\n query: { ip },\n });\n }\n\n /**\n * Reports whether an IP address is barred from starting an order flow.\n *\n * Covers jurisdictions your organisation is not licensed for, along with known\n * proxies and anonymisers. Call it as early as the landing page: refusing a\n * visitor before they invest time in a questionnaire is better for them and\n * cheaper for you.\n *\n * @param ip The visitor's IP address, v4 or v6.\n * @returns The blocklist verdict.\n * @throws {ValidationError} If the address is malformed.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async blocklist<T = Record<string, unknown>>(ip: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'platform',\n method: 'GET',\n path: '/extensions/geo-blocklist',\n query: { ip },\n });\n }\n}\n","import { readFile, stat } from 'node:fs/promises';\nimport { basename, extname } from 'node:path';\n\n/**\n * Extension-to-MIME map covering the types the file endpoints accept.\n *\n * An explicit table rather than content sniffing, because sniffing misreports\n * some of these: a `.docx` is a ZIP container on disk and sniffs as\n * `application/zip`, which the server rejects.\n */\nconst MIME_TYPES: Readonly<Record<string, string>> = Object.freeze({\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n webp: 'image/webp',\n gif: 'image/gif',\n pdf: 'application/pdf',\n doc: 'application/msword',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n txt: 'text/plain',\n mp4: 'video/mp4',\n mov: 'video/quicktime',\n webm: 'video/webm',\n});\n\n/** MIME type used when the file name does not identify the content. */\nconst FALLBACK_MIME_TYPE = 'application/octet-stream';\n\n/** Overrides accepted by {@link FileUpload.fromPath}. */\nexport interface FileUploadFromPathOptions {\n /** Name to send in the part header. Defaults to the path's base name. */\n fileName?: string;\n /** MIME type to send. Defaults to one derived from the extension. */\n mimeType?: string;\n}\n\n/** Overrides accepted by {@link FileUpload.fromContents}. */\nexport interface FileUploadFromContentsOptions {\n /** MIME type to send. Defaults to one derived from the file name. */\n mimeType?: string;\n}\n\n/**\n * An immutable file, in memory, ready to be sent as a `multipart/form-data`\n * part.\n *\n * A handful of AsterMD endpoints take bytes rather than JSON - the\n * intake-submission file endpoints on the `sales` service are the current\n * example. Those bytes come from very different places: a file your users just\n * uploaded and that now sits on local disk, or a slice of a large file being fed\n * through the multipart flow one chunk at a time. This class is the single shape\n * resource methods accept for both, so {@link Transport} has exactly one thing\n * to encode.\n *\n * Build it through one of the two factories - the constructor is private, so an\n * instance always carries a file name and a MIME type:\n *\n * ```ts\n * // From a file on disk; name and MIME type come from the path.\n * await client.intakeSubmissions().uploadFile(\n * sessionId,\n * await FileUpload.fromPath('/var/uploads/id-front.jpg'),\n * );\n *\n * // From bytes already in memory: one 10 MB slice of a large video.\n * await client.intakeSubmissions().uploadMultipartPart({\n * uploadId,\n * partNumber: 1,\n * part: FileUpload.fromContents(chunk, 'consult-video.mp4'),\n * });\n * ```\n *\n * The whole file is held in memory, so it is deliberately not the right tool for\n * a 200 MB video - use {@link IntakeSubmissions.uploadLargeFile}, which streams\n * from disk and only ever materialises one part at a time.\n *\n * File names are normalised on construction: directory components are dropped\n * and quote, backslash, and newline characters removed, so a caller-supplied\n * name can never break out of the header it is written into.\n */\nexport class FileUpload {\n readonly #contents: Uint8Array;\n readonly #fileName: string;\n readonly #mimeType: string;\n\n private constructor(contents: Uint8Array, fileName: string, mimeType: string) {\n this.#contents = contents;\n this.#fileName = fileName;\n this.#mimeType = mimeType;\n\n Object.freeze(this);\n }\n\n /**\n * Reads a file from local disk into an upload.\n *\n * The file is read in full and held in memory, so keep this for the sizes the\n * single-shot endpoints allow. Without `options.fileName` the path's base name\n * is used, and without `options.mimeType` the type is derived from the\n * extension, falling back to `application/octet-stream`.\n *\n * @param path Path to an existing, readable file.\n * @param options Optional name and MIME type overrides.\n * @returns An upload carrying the file's bytes, name, and MIME type.\n * @throws {TypeError} If `path` is not a readable file, or if the resulting\n * file name normalises to nothing usable.\n */\n static async fromPath(path: string, options: FileUploadFromPathOptions = {}): Promise<FileUpload> {\n let isFile: boolean;\n\n try {\n isFile = (await stat(path)).isFile();\n } catch {\n isFile = false;\n }\n\n if (!isFile) {\n throw new TypeError(`File is not readable: ${path}`);\n }\n\n let contents: Buffer;\n\n try {\n contents = await readFile(path);\n } catch (error) {\n throw new TypeError(`Failed to read file: ${path}`, { cause: error });\n }\n\n const name = normaliseFileName(options.fileName ?? basename(path));\n\n return new FileUpload(new Uint8Array(contents), name, options.mimeType ?? guessMimeType(name));\n }\n\n /**\n * Wraps bytes already held in memory into an upload.\n *\n * Use this when the bytes never came from a file on disk: a slice of a larger\n * file being sent through the multipart flow, a payload received over the\n * wire, or content generated at runtime. `fileName` is required because there\n * is no path to derive one from.\n *\n * @param contents The raw bytes to send as the part body.\n * @param fileName Name to send in the part header.\n * @param options Optional MIME type override.\n * @returns An upload carrying the given bytes, name, and MIME type.\n * @throws {TypeError} If `fileName` normalises to nothing usable.\n */\n static fromContents(contents: Uint8Array, fileName: string, options: FileUploadFromContentsOptions = {}): FileUpload {\n const name = normaliseFileName(fileName);\n\n return new FileUpload(contents, name, options.mimeType ?? guessMimeType(name));\n }\n\n /**\n * Returns the raw bytes that will form the body of the multipart part.\n *\n * @returns The file's contents.\n */\n contents(): Uint8Array {\n return this.#contents;\n }\n\n /**\n * Returns the normalised file name sent in the part header.\n *\n * The server uses this to derive the stored object name, inserting a unique\n * identifier before the extension so repeat uploads of the same name never\n * collide.\n *\n * @returns The file name, stripped of directory components and\n * header-breaking characters.\n */\n fileName(): string {\n return this.#fileName;\n }\n\n /**\n * Returns the MIME type sent for the part.\n *\n * @returns The MIME type, e.g. `image/jpeg`.\n */\n mimeType(): string {\n return this.#mimeType;\n }\n\n /**\n * Returns the size of the upload in bytes.\n *\n * Useful for checking a file against an endpoint's documented limit before\n * spending a round trip on it, and for the `fileSize` value the multipart\n * initiate call requires.\n *\n * @returns The number of bytes in {@link FileUpload.contents}.\n */\n size(): number {\n return this.#contents.byteLength;\n }\n}\n\n/**\n * Drops directory components, then strips the characters that would let a\n * caller-supplied name terminate or forge the part header it is written into.\n *\n * @throws {TypeError} If nothing usable remains.\n */\nfunction normaliseFileName(fileName: string): string {\n const stripped = fileName.trim().replace(/[\\r\\n\"\\\\]/g, '');\n const name = basename(stripped).trim();\n\n if (name === '' || name === '.' || name === '..') {\n throw new TypeError('File name must not be empty.');\n }\n\n return name;\n}\n\nfunction guessMimeType(fileName: string): string {\n const extension = extname(fileName).replace(/^\\./, '').toLowerCase();\n\n return MIME_TYPES[extension] ?? FALLBACK_MIME_TYPE;\n}\n","import { open, stat } from 'node:fs/promises';\nimport type { FileHandle } from 'node:fs/promises';\nimport { basename } from 'node:path';\nimport type { Event } from '../enum/event.js';\nimport { ApiError } from '../errors/index.js';\nimport { FileUpload } from '../http/file-upload.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/** Options for {@link IntakeSubmissions.view}. */\nexport interface IntakeViewOptions {\n /** The intake submission's id. */\n id: string;\n /** The teleform the submission answers. */\n teleformId: string;\n}\n\n/** Options for {@link IntakeSubmissions.create} and {@link IntakeSubmissions.update}. */\nexport interface IntakeWriteOptions {\n /** The session the submission belongs to. */\n session: string;\n /** The journey event this call records. */\n event: Event;\n /** The teleform being answered. */\n teleformId: string;\n /**\n * The answers, one entry per field: `{ id, name, label, type, value }`, where\n * `value` is itself a list of `{ value }` entries so a multi-select field can\n * carry several. Send every answer collected so far, not only what changed.\n */\n data: Record<string, unknown>[];\n /**\n * Optional progress information for a partially completed form, e.g.\n * `{ current_step, total_steps }`. Lets the server report how far a prospect\n * got before abandoning.\n */\n progress?: Record<string, unknown>;\n}\n\n/** Options for {@link IntakeSubmissions.uploadFile}. */\nexport interface UploadFileOptions {\n /** The session the file belongs to. */\n sessionId: string;\n /** The file to upload. */\n file: FileUpload;\n}\n\n/** Options for {@link IntakeSubmissions.initiateMultipartUpload}. */\nexport interface InitiateMultipartOptions {\n /** The session the file belongs to. */\n sessionId: string;\n /** Name to store the file under. */\n fileName: string;\n /** MIME type of the whole file. */\n mimeType: string;\n /** Total size of the whole file in bytes, not of one part. */\n fileSize: number;\n}\n\n/** Options for {@link IntakeSubmissions.uploadMultipartPart}. */\nexport interface UploadPartOptions {\n /** The upload id returned by initiate. */\n uploadId: string;\n /** 1-based part number, between 1 and 10000. */\n partNumber: number;\n /** The bytes of this part. */\n part: FileUpload;\n}\n\n/** One uploaded part, as {@link IntakeSubmissions.finishMultipartUpload} expects it. */\nexport interface MultipartPart {\n /** The part number that was uploaded. */\n part_number: number;\n /** The etag the server returned for that part. */\n etag: string;\n}\n\n/** Options for {@link IntakeSubmissions.finishMultipartUpload}. */\nexport interface FinishMultipartOptions {\n /** The upload id returned by initiate. */\n uploadId: string;\n /** Every uploaded part with its etag, in order. Must not be empty. */\n parts: MultipartPart[];\n}\n\n/** Options for {@link IntakeSubmissions.uploadLargeFile}. */\nexport interface UploadLargeFileOptions {\n /** The session the file belongs to. */\n sessionId: string;\n /** Path to the file on local disk. */\n filePath: string;\n /** Name to store the file under. Defaults to the path's base name. */\n fileName?: string;\n /** MIME type to send. Defaults to one derived from the file name. */\n mimeType?: string;\n /**\n * Bytes per part. Must be between {@link IntakeSubmissions.MIN_PART_SIZE} and\n * {@link IntakeSubmissions.MAX_PART_SIZE}. Defaults to\n * {@link IntakeSubmissions.DEFAULT_PART_SIZE}.\n */\n partSize?: number;\n}\n\n/**\n * Intake submissions - the answers a prospect gives to your questionnaires.\n *\n * This is where the clinical part of the order flow happens. Two questionnaires\n * run through this one resource: the short pre-qualifying form that decides\n * whether a prospect is eligible at all, and the full intake questionnaire a\n * clinician later reviews. Which one a call belongs to is carried entirely by the\n * {@link Event} it passes.\n *\n * Both follow the same rhythm. The `*Initiated` event goes to\n * {@link IntakeSubmissions.create}, which creates the server-side record. Every\n * later step goes to {@link IntakeSubmissions.update} with `*InProgress`, and the\n * final submission with `*Completed`. Sending progress updates as the prospect\n * moves through the form is what makes partial completion visible instead of\n * looking like an abandoned session:\n *\n * ```ts\n * await client.intakeSubmissions().create({\n * session,\n * event: Event.IntakeInitiated,\n * teleformId,\n * data: answersSoFar,\n * });\n *\n * await client.intakeSubmissions().update({\n * session,\n * event: Event.IntakeCompleted,\n * teleformId,\n * data: allAnswers,\n * });\n * ```\n *\n * `data` is cumulative: send every answer collected so far on each call, not just\n * the fields that changed. Answers that are files rather than values go through\n * the upload methods on this same resource.\n */\nexport class IntakeSubmissions extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Fetches one intake submission and its recorded answers.\n *\n * The teleform id is required alongside the submission id because a submission\n * is only meaningful against the form it answers - the server uses it to\n * resolve field definitions for the stored values.\n *\n * @param options The submission id and the teleform it answers.\n * @returns The submission and its answers.\n * @throws {NotFoundError} If the submission does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(options: IntakeViewOptions): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/intake-submissions/view/{id}',\n pathParams: { id: options.id },\n query: { teleform_id: options.teleformId },\n });\n }\n\n /**\n * Creates the intake-submission record for a session and records the first\n * answers.\n *\n * This is the call that opens the record, so it takes an `*Initiated` event -\n * `Event.PreQualifyingInitiated` or `Event.IntakeInitiated`. Every later step\n * goes to {@link IntakeSubmissions.update}. The server advances its order-flow\n * state machine on the event, so passing the wrong one puts the flow in the\n * wrong state rather than failing loudly.\n *\n * @param options The session, event, teleform, answers, and optional progress.\n * @returns The created submission.\n * @throws {ValidationError} If the answers or the event are rejected.\n * @throws {NotFoundError} If the session or teleform does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async create<T = Record<string, unknown>>(options: IntakeWriteOptions): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/intake-submissions/create',\n body: intakeBody(options),\n });\n }\n\n /**\n * Records further answers against an existing intake submission.\n *\n * Takes an `*InProgress` event for an intermediate step or a `*Completed` event\n * for the final submission. `data` is cumulative - send every answer collected\n * so far, since the server treats the payload as the current state of the form\n * rather than a patch.\n *\n * @param options The session, event, teleform, answers, and optional progress.\n * @returns The updated submission.\n * @throws {ValidationError} If the answers or the event are rejected.\n * @throws {NotFoundError} If no submission exists for the session.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async update<T = Record<string, unknown>>(options: IntakeWriteOptions): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PUT',\n path: '/intake-submissions/update/{session}',\n pathParams: { session: options.session },\n body: intakeBody(options),\n });\n }\n\n /** Smallest part the multipart endpoint accepts: 5 MB. */\n static readonly MIN_PART_SIZE = 5 * 1024 * 1024;\n\n /** Largest part the multipart endpoint accepts: 25 MB. */\n static readonly MAX_PART_SIZE = 25 * 1024 * 1024;\n\n /** Part size {@link IntakeSubmissions.uploadLargeFile} uses by default: 10 MB. */\n static readonly DEFAULT_PART_SIZE = 10 * 1024 * 1024;\n\n /**\n * Uploads one file in a single request, for a file-type answer.\n *\n * Use this for the sizes a single request can carry - a photograph of an ID, a\n * PDF. The whole file is held in memory and sent as one `multipart/form-data`\n * body. For anything large enough to risk a timeout, use\n * {@link IntakeSubmissions.uploadLargeFile} instead.\n *\n * The response carries the stored file's reference, which is what you place in\n * the `value` of the corresponding field when you next call\n * {@link IntakeSubmissions.update}.\n *\n * @param options The session and the file.\n * @returns The stored file's reference.\n * @throws {ValidationError} If the file type or size is rejected.\n * @throws {NotFoundError} If the session does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async uploadFile<T = Record<string, unknown>>(options: UploadFileOptions): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/intake-submissions/upload-file/{session_id}',\n pathParams: { session_id: options.sessionId },\n file: options.file,\n });\n }\n\n /**\n * Opens a multipart upload and returns the id the parts will be sent under.\n *\n * The first of the four low-level multipart calls. Declare the *whole* file's\n * name, type, and size here - the server uses the size to validate the parts it\n * later receives. Prefer {@link IntakeSubmissions.uploadLargeFile}, which\n * drives all four calls; reach for these directly only when you need to\n * control the loop, for instance to upload parts from several workers.\n *\n * @param options The session and the whole file's metadata.\n * @returns The opened upload; `data().upload_id` carries the id.\n * @throws {ValidationError} If the metadata is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async initiateMultipartUpload<T = Record<string, unknown>>(\n options: InitiateMultipartOptions,\n ): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/intake-submissions/upload-file-multipart/initiate/{session_id}',\n pathParams: { session_id: options.sessionId },\n body: {\n file_name: options.fileName,\n mime_type: options.mimeType,\n file_size: options.fileSize,\n },\n });\n }\n\n /**\n * Uploads one part of an open multipart upload.\n *\n * Part numbers are 1-based and must arrive with the part they label; the\n * response carries an etag you must keep, because\n * {@link IntakeSubmissions.finishMultipartUpload} needs every part's etag to\n * assemble the file. Parts may be uploaded in any order.\n *\n * @param options The upload id, the part number, and the part's bytes.\n * @returns The stored part; `data().etag` carries the etag.\n * @throws {TypeError} If `partNumber` is outside 1–10000.\n * @throws {NotFoundError} If the upload id is unknown or already finished.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async uploadMultipartPart<T = Record<string, unknown>>(options: UploadPartOptions): Promise<ApiResponse<T>> {\n if (options.partNumber < 1 || options.partNumber > 10_000) {\n throw new TypeError(`Part number must be between 1 and 10000, got ${options.partNumber}.`);\n }\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/intake-submissions/upload-file-multipart/part/{upload_id}',\n pathParams: { upload_id: options.uploadId },\n query: { part_number: options.partNumber },\n file: options.part,\n });\n }\n\n /**\n * Closes a multipart upload, assembling the parts into one stored file.\n *\n * Send every part with the etag its own upload returned. The server verifies\n * the set against what it received, so a missing or mismatched part fails here\n * rather than producing a corrupt file.\n *\n * @param options The upload id and every part with its etag.\n * @returns The stored file's reference.\n * @throws {TypeError} If `parts` is empty.\n * @throws {ValidationError} If the parts do not match what the server holds.\n * @throws {NotFoundError} If the upload id is unknown.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async finishMultipartUpload<T = Record<string, unknown>>(options: FinishMultipartOptions): Promise<ApiResponse<T>> {\n if (options.parts.length === 0) {\n throw new TypeError('At least one part is required to finish a multipart upload.');\n }\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/intake-submissions/upload-file-multipart/finish/{upload_id}',\n pathParams: { upload_id: options.uploadId },\n body: { parts: options.parts },\n });\n }\n\n /**\n * Abandons a multipart upload and discards the parts already stored.\n *\n * Call this when an upload cannot be completed, so the parts do not linger.\n * {@link IntakeSubmissions.uploadLargeFile} does it for you on failure.\n *\n * @param uploadId The upload id to abandon.\n * @returns The abort confirmation.\n * @throws {NotFoundError} If the upload id is unknown or already finished.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async abortMultipartUpload<T = Record<string, unknown>>(uploadId: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/intake-submissions/upload-file-multipart/abort/{upload_id}',\n pathParams: { upload_id: uploadId },\n });\n }\n\n /**\n * Uploads a large file from disk, driving the whole multipart flow.\n *\n * This is the method to reach for when a file is too big for one request. It\n * streams the file one part at a time, so a 200 MB video costs one part of\n * memory rather than 200 MB: initiate, then read a part, upload it, keep its\n * etag, repeat, then finish. If any step fails the upload is aborted so no\n * parts are left behind, and the failure that broke the upload is what\n * surfaces - not the abort's own outcome.\n *\n * The first part fixes the name and MIME type for the whole upload, so\n * `initiate` and every part agree.\n *\n * ```ts\n * const stored = await client.intakeSubmissions().uploadLargeFile({\n * sessionId,\n * filePath: '/var/uploads/consult-video.mp4',\n * });\n * ```\n *\n * @param options The session, the file's path, and optional name, MIME type,\n * and part size.\n * @returns The finished upload's response - the same one\n * {@link IntakeSubmissions.finishMultipartUpload} returns.\n * @throws {TypeError} If `partSize` is outside the allowed range, or if the\n * file is missing, unreadable, or empty.\n * @throws {ApiError} If the server response omits a usable `upload_id` or\n * `etag`, or on any other non-2xx status.\n * @throws {TransportError} If a request never completed.\n */\n async uploadLargeFile<T = Record<string, unknown>>(options: UploadLargeFileOptions): Promise<ApiResponse<T>> {\n const partSize = options.partSize ?? IntakeSubmissions.DEFAULT_PART_SIZE;\n\n if (partSize < IntakeSubmissions.MIN_PART_SIZE || partSize > IntakeSubmissions.MAX_PART_SIZE) {\n throw new TypeError(\n `Part size must be between ${IntakeSubmissions.MIN_PART_SIZE} and ` +\n `${IntakeSubmissions.MAX_PART_SIZE} bytes, got ${partSize}.`,\n );\n }\n\n let fileSize: number;\n\n try {\n const stats = await stat(options.filePath);\n\n if (!stats.isFile()) {\n throw new TypeError(`File is not readable: ${options.filePath}`);\n }\n\n fileSize = stats.size;\n } catch (error) {\n if (error instanceof TypeError) {\n throw error;\n }\n\n throw new TypeError(`File is not readable: ${options.filePath}`, {\n cause: error,\n });\n }\n\n if (fileSize === 0) {\n throw new TypeError(`File is empty: ${options.filePath}`);\n }\n\n const handle = await open(options.filePath, 'r');\n\n try {\n const buffer = Buffer.alloc(partSize);\n const firstBytesRead = await readFull(handle, buffer, partSize);\n\n if (firstBytesRead === 0) {\n throw new TypeError(`File is empty: ${options.filePath}`);\n }\n\n // The first part fixes the name and MIME type for the whole upload, so\n // initiate agrees with every part that follows it.\n //\n // Copy the chunk out of the reused read buffer. `Buffer.prototype.slice`\n // is an alias for `subarray` and returns a view, not a copy. Today that\n // would still be safe, because Transport snapshots each part into a Blob\n // before the next read refills the buffer - but the copy keeps this\n // correct independently of send ordering, so a later change that reads\n // ahead or batches parts cannot silently ship the wrong bytes.\n let part = FileUpload.fromContents(\n new Uint8Array(buffer.subarray(0, firstBytesRead)),\n options.fileName ?? basename(options.filePath),\n options.mimeType === undefined ? {} : { mimeType: options.mimeType },\n );\n\n const uploadId = stringField(\n await this.initiateMultipartUpload({\n sessionId: options.sessionId,\n fileName: part.fileName(),\n mimeType: part.mimeType(),\n fileSize,\n }),\n 'upload_id',\n );\n\n try {\n const parts: MultipartPart[] = [];\n let partNumber = 1;\n\n for (;;) {\n const result = await this.uploadMultipartPart({\n uploadId,\n partNumber,\n part,\n });\n parts.push({\n part_number: partNumber,\n etag: stringField(result, 'etag'),\n });\n\n const nextBytesRead = await readFull(handle, buffer, partSize);\n\n if (nextBytesRead === 0) {\n break;\n }\n\n partNumber += 1;\n // Same reused buffer as above: copy the chunk out for the same reason\n // - safe today because of send ordering, but not something to rely on.\n part = FileUpload.fromContents(new Uint8Array(buffer.subarray(0, nextBytesRead)), part.fileName(), {\n mimeType: part.mimeType(),\n });\n }\n\n return await this.finishMultipartUpload<T>({ uploadId, parts });\n } catch (error) {\n try {\n await this.abortMultipartUpload(uploadId);\n } catch {\n // Surface the failure that broke the upload, not the cleanup's.\n }\n\n throw error;\n }\n } finally {\n await handle.close().catch(() => undefined);\n }\n }\n}\n\n/**\n * Reads until the buffer is full or the file ends.\n *\n * A single `read()` may return fewer bytes than asked for even mid-file - POSIX\n * permits it and network and overlay filesystems do it - which would produce a\n * part below the minimum size and fail the upload only at the finish step.\n */\nasync function readFull(handle: FileHandle, buffer: Buffer, length: number): Promise<number> {\n let filled = 0;\n\n while (filled < length) {\n const { bytesRead } = await handle.read(buffer, filled, length - filled, null);\n\n if (bytesRead === 0) {\n break;\n }\n\n filled += bytesRead;\n }\n\n return filled;\n}\n\n/**\n * Builds the request body shared by create and update.\n *\n * The session appears in the body as well as the path on update; that is what the\n * server expects, so it is not redundant.\n */\nfunction intakeBody(options: IntakeWriteOptions): Record<string, unknown> {\n const body: Record<string, unknown> = {\n session: options.session,\n event: options.event,\n teleform_id: options.teleformId,\n data: options.data,\n };\n\n if (options.progress !== undefined) {\n body.progress = options.progress;\n }\n\n return body;\n}\n\n/**\n * Reads a required string out of a response payload.\n *\n * The multipart flow depends on two server-supplied values - the upload id and\n * each part's etag - and continuing without one produces a corrupt upload rather\n * than an error. So a missing or empty value fails here, loudly.\n */\nfunction stringField(response: ApiResponse, key: string): string {\n const value = response.data()[key];\n\n if (typeof value !== 'string' || value === '') {\n throw new ApiError(`The server response did not include a usable ${key}.`, response.statusCode(), {});\n }\n\n return value;\n}\n","import type { QueryParams } from '../http/url-builder.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Lab tests - the diagnostic panels a product may require.\n *\n * Some treatments cannot be prescribed without a result: a blood panel, a\n * hormone level. A lab test is the server-side definition of one such\n * requirement, and products reference the tests they depend on. A storefront reads\n * this to tell a buyer, before they pay, that a test will be part of their\n * treatment.\n *\n * Read-only, and configured in your AsterMD dashboard.\n */\nexport class LabTests extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Lists lab tests, filtered and paginated.\n *\n * Pagination details come back in `meta()`. The accepted filters are in the\n * API reference in your AsterMD dashboard.\n *\n * @param query Filters and pagination parameters.\n * @returns The matching lab tests, with pagination in `meta()`.\n * @throws {ValidationError} If a filter is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async list<T = Record<string, unknown>>(query: QueryParams = {}): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/lab-tests/list',\n query,\n });\n }\n\n /**\n * Fetches one lab test.\n *\n * @param id The lab test's id.\n * @returns The lab test record.\n * @throws {NotFoundError} If the lab test does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/lab-tests/view/{id}',\n pathParams: { id },\n });\n }\n}\n","import type { QueryParams } from '../http/url-builder.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Medications - the reference list of drugs the platform knows about.\n *\n * This is a lookup table, not a catalogue of things to sell: it is what a\n * storefront renders when an intake form asks \"which medications are you\n * currently taking?\" Answers picked from this list arrive at the clinician as\n * recognised drugs rather than free text, which is the difference between a\n * reviewable history and a paragraph someone has to interpret.\n *\n * List-only by design - the API exposes no single-medication endpoint, so\n * neither does the SDK. Filter through the query parameters to implement a\n * type-ahead rather than downloading the whole list.\n */\nexport class Medications extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Lists medications, filtered and paginated.\n *\n * Intended to back a search or type-ahead on an intake form. Pagination comes\n * back in `meta()`; the accepted filters are in the API reference in your\n * AsterMD dashboard.\n *\n * @param query Filters and pagination parameters.\n * @returns The matching medications, with pagination in `meta()`.\n * @throws {ValidationError} If a filter is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async list<T = Record<string, unknown>>(query: QueryParams = {}): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/medications/list',\n query,\n });\n }\n}\n","import type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Opportunities - a prospect who has identified themselves and is worth\n * following up.\n *\n * This is the commercial record of the order flow, and the point where an\n * anonymous session becomes a named person. Create it once you have a name and a\n * contact address, typically right after the pre-qualifying form, and attach the\n * sessions that led to it so the whole journey stays connected:\n *\n * ```ts\n * const opportunity = await client.opportunities().create({\n * first_name: 'Jane',\n * email: 'jane@example.test',\n * sessions: [session],\n * });\n * ```\n *\n * Attaching sessions is what makes attribution work: without them the server has\n * a lead with no idea where it came from. An opportunity can carry several\n * sessions, which is what you want when a prospect returns across devices or\n * days.\n *\n * Where this record is commercial, {@link Patients} is its clinical counterpart -\n * created later, once the prospect is actually proceeding with treatment.\n */\nexport class Opportunities extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Creates an opportunity.\n *\n * Include a `sessions` array of the session identifiers that led here, so the\n * server can attribute the lead. The rest of the payload is forwarded\n * unmodified.\n *\n * @param data The prospect's details and the sessions to attach.\n * @returns The created opportunity, including its id.\n * @throws {ValidationError} If a field is missing or rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async create<T = Record<string, unknown>>(data: Record<string, unknown>): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/opportunities/create',\n body: data,\n });\n }\n\n /**\n * Fetches one opportunity.\n *\n * @param id The opportunity's id.\n * @returns The opportunity record.\n * @throws {NotFoundError} If the opportunity does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/opportunities/view/{id}',\n pathParams: { id },\n });\n }\n\n /**\n * Updates an opportunity.\n *\n * Send only the fields you are changing. Use this to enrich a lead as more\n * information arrives - a phone number, a later session to attach.\n *\n * @param id The opportunity's id.\n * @param data The fields to change.\n * @returns The updated opportunity.\n * @throws {NotFoundError} If the opportunity does not exist.\n * @throws {ValidationError} If a field is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async update<T = Record<string, unknown>>(id: string, data: Record<string, unknown>): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PUT',\n path: '/opportunities/update/{id}',\n pathParams: { id },\n body: data,\n });\n }\n\n /**\n * Changes an opportunity's status.\n *\n * Kept separate from {@link Opportunities.update} so a pipeline transition is\n * explicit in your code and in the server's audit trail. The accepted values\n * are configured for your organisation; see the API reference in your AsterMD\n * dashboard.\n *\n * @param id The opportunity's id.\n * @param status The new status value, sent as `opportunity_status`.\n * @returns The updated opportunity.\n * @throws {NotFoundError} If the opportunity does not exist.\n * @throws {ValidationError} If the status is not an accepted value.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async status<T = Record<string, unknown>>(id: string, status: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PATCH',\n path: '/opportunities/status/{id}',\n pathParams: { id },\n body: { opportunity_status: status },\n });\n }\n}\n","import type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/** Options for {@link Patients.submitHealthInformation}. */\nexport interface SubmitHealthInformationOptions {\n /**\n * The health information to record. Forwarded unmodified; see the API\n * reference in your AsterMD dashboard for the accepted fields.\n */\n data: Record<string, unknown>;\n /**\n * Verification token proving the patient authorised this submission, sent as\n * the `x-phi-verification-token` header. Required by the server for\n * submissions that need explicit patient confirmation.\n */\n phiVerificationToken?: string;\n}\n\n/**\n * Patients - the person receiving care, and the most sensitive data in the SDK.\n *\n * A patient record is created once the prospect has qualified and is proceeding\n * with treatment, and it is what a clinician's review, a prescription, and an\n * order all hang from. Where an {@link Opportunities} record is a commercial\n * artifact, this is a clinical one.\n *\n * Everything on this resource carries personal and protected health information.\n * Two consequences worth knowing about:\n *\n * The SDK never writes a request or response body from any `patients/*` endpoint\n * to a debug log, even with `debugRedact: false` - those bodies are dropped\n * wholesale rather than masked. Debug entries for these calls show the method,\n * URL, and status only.\n *\n * Health-information submission is a two-step flow when the server requires\n * explicit patient confirmation: {@link Patients.verifyHealthInformationOtp}\n * confirms a one-time code the patient received, and the resulting token is\n * passed to {@link Patients.submitHealthInformation} as\n * `phiVerificationToken`.\n *\n * Handling this data does not by itself make your application HIPAA compliant -\n * that depends on your own infrastructure, policies, and agreements. Contact\n * info@astermd.com regarding a Business Associate Agreement.\n */\nexport class Patients extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Creates a patient record.\n *\n * Call this once the prospect has qualified and is proceeding. The payload is\n * forwarded unmodified, so it carries whatever demographic and contact fields\n * your organisation's configuration requires.\n *\n * @param data The patient's details. See the API reference in your AsterMD\n * dashboard for the accepted fields.\n * @returns The created patient; `data()` carries the new record including its id.\n * @throws {ValidationError} If a field is missing or rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async create<T = Record<string, unknown>>(data: Record<string, unknown>): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/patients/create',\n body: data,\n });\n }\n\n /**\n * Fetches one patient record.\n *\n * Returns the record as your credentials are permitted to see it. Because the\n * organisation scope lives inside the JWT, an id belonging to another\n * organisation is indistinguishable from one that never existed - both raise\n * {@link NotFoundError}.\n *\n * @param id The patient's id.\n * @returns The patient record.\n * @throws {NotFoundError} If the patient does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/patients/view/{id}',\n pathParams: { id },\n });\n }\n\n /**\n * Updates a patient record.\n *\n * Send only the fields you are changing; the server merges them into the\n * existing record rather than replacing it.\n *\n * @param id The patient's id.\n * @param data The fields to change.\n * @returns The updated patient record.\n * @throws {NotFoundError} If the patient does not exist.\n * @throws {ValidationError} If a field is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async update<T = Record<string, unknown>>(id: string, data: Record<string, unknown>): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PUT',\n path: '/patients/update/{id}',\n pathParams: { id },\n body: data,\n });\n }\n\n /**\n * Changes a patient's status.\n *\n * A narrow call for the one field that drives workflow state, kept separate\n * from {@link Patients.update} so a status transition is explicit in your code\n * and in the server's audit trail. The accepted values are configured for your\n * organisation; see the API reference in your AsterMD dashboard.\n *\n * @param id The patient's id.\n * @param status The new status value.\n * @returns The updated patient record.\n * @throws {NotFoundError} If the patient does not exist.\n * @throws {ValidationError} If the status is not an accepted value.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async status<T = Record<string, unknown>>(id: string, status: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PATCH',\n path: '/patients/status/{id}',\n pathParams: { id },\n body: { status },\n });\n }\n\n /**\n * Submits a patient's health information.\n *\n * This is the clinical questionnaire result a clinician reviews, and the most\n * sensitive payload the SDK carries. Where the server requires explicit patient\n * confirmation, obtain a token through\n * {@link Patients.verifyHealthInformationOtp} first and pass it as\n * `phiVerificationToken`.\n *\n * Neither the request nor the response body is ever written to a debug log.\n *\n * @param options The health information, and the verification token where one\n * is required.\n * @returns The stored submission.\n * @throws {AuthenticationError} If the verification token is missing or invalid.\n * @throws {ValidationError} If the payload is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async submitHealthInformation<T = Record<string, unknown>>(\n options: SubmitHealthInformationOptions,\n ): Promise<ApiResponse<T>> {\n const headers =\n options.phiVerificationToken !== undefined ? { 'x-phi-verification-token': options.phiVerificationToken } : {};\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/patients/health-information',\n body: options.data,\n headers,\n });\n }\n\n /**\n * Verifies the one-time code a patient was sent, before health information is\n * submitted.\n *\n * The confirming half of the two-step health-information flow. On success the\n * response carries the verification token to pass to\n * {@link Patients.submitHealthInformation}.\n *\n * The one-time code is a credential: it is never written to a debug log, and it\n * should not be written to yours either.\n *\n * @param data The patient identifier and the one-time code. See the API\n * reference in your AsterMD dashboard for the exact fields.\n * @returns The verification result, carrying the token on success.\n * @throws {ValidationError} If the code is wrong, expired, or malformed.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async verifyHealthInformationOtp<T = Record<string, unknown>>(\n data: Record<string, unknown>,\n ): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/patients/health-information/otp-verification',\n body: data,\n });\n }\n}\n","import type { QueryParams } from '../http/url-builder.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Products - the read-only catalogue your storefront sells from.\n *\n * A product carries everything a storefront needs to render it: names,\n * descriptions, images, variants, prices, purchase quantity limits, and the\n * questionnaire a buyer must complete. All of it is configured in your AsterMD\n * dashboard, and the SDK only reads it - a storefront never creates or edits a\n * product.\n *\n * Which products a given selling surface may offer is a channel decision, so\n * {@link Channels.assignedProducts} and {@link Channels.details} are usually the\n * better starting point for a catalogue page. Reach for this resource when you\n * need the full product list regardless of channel, or one product by id.\n *\n * Image and asset paths come back relative. Resolve them with\n * `client.config().assetUrl(path)`, and treat the result as a fetch URL: download\n * the asset once and serve your own copy rather than hot-linking.\n */\nexport class Products extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Lists products, filtered and paginated.\n *\n * Pagination details come back in `meta()`. The accepted filters are in the\n * API reference in your AsterMD dashboard.\n *\n * @param query Filters and pagination parameters.\n * @returns The matching products, with pagination in `meta()`.\n * @throws {ValidationError} If a filter is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async list<T = Record<string, unknown>>(query: QueryParams = {}): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/products/list',\n query,\n });\n }\n\n /**\n * Fetches one product, including its variants and prices.\n *\n * @param id The product's id.\n * @returns The product record.\n * @throws {NotFoundError} If the product does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/products/view/{id}',\n pathParams: { id },\n });\n }\n}\n","import type { Response as ApiResponse } from '../response.js';\nimport type { Transport } from '../http/transport.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/** Options for {@link Sessions.create}. */\nexport interface SessionCreateOptions {\n /**\n * Attribution and context to record against the session - UTM parameters,\n * referrer, landing page. Forwarded unmodified; see the API reference in your\n * AsterMD dashboard for the recognised keys. May include an optional\n * `verification` object recording identity/contact verification already\n * performed by the caller - every field inside it is optional too: `email`\n * (boolean), `address` (boolean), and `id` (`verified` boolean, `method` -\n * `ssn` | `dob` | `cross_check` | `document_upload`, `value` string).\n */\n data?: Record<string, unknown>;\n /** The visitor's user agent, forwarded as the `User-Agent` header. */\n userAgent?: string;\n /** The visitor's IP address, forwarded as the `X-Original-Client-Ip` header. */\n clientIp?: string;\n}\n\n/** Options for {@link Sessions.view}. */\nexport interface SessionViewOptions {\n /** Session identifiers to fetch. Sent as a comma-separated `session_ids` query. */\n sessions: string[];\n /** IANA time zone used to render timestamps in the response. */\n tz?: string;\n}\n\n/**\n * Sessions - the thread that ties one visitor's whole journey together.\n *\n * A session is the first thing an order flow creates and the value every later\n * call is attached to: intake submissions, the cart, checkout events, and finally\n * the opportunity. Creating one also records an implicit `visit_page` event\n * server-side, so the funnel starts measuring from that moment.\n *\n * Create the session as early as the visitor's first page view, keep the returned\n * identifier for the length of their visit - in a cookie or your own server-side\n * session - and pass it to every subsequent call:\n *\n * ```ts\n * const { session } = (\n * await client.sessions().create<{ session: string }>({\n * userAgent: request.headers['user-agent'],\n * clientIp: request.ip,\n * })\n * ).data();\n * ```\n *\n * Passing the visitor's user agent and IP matters: the server uses them for\n * attribution and geographic checks, and without them every session appears to\n * originate from your server rather than from the visitor.\n */\nexport class Sessions extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Creates a new session and returns its identifier.\n *\n * This is the first call of an order flow. The server records an implicit\n * `visit_page` event alongside the new session, so the funnel begins here.\n * Inspect `data().session` on the response for the identifier to thread through\n * every later call.\n *\n * @param options Attribution payload and visitor headers. All optional; with\n * none, an empty object body is sent.\n * @returns The created session; `data()` holds `{ session }`.\n * @throws {ValidationError} If the attribution payload is rejected.\n * @throws {AuthenticationError} If the credentials are refused.\n * @throws {RateLimitError} If the organisation's request allowance is exceeded.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async create<T = Record<string, unknown>>(options: SessionCreateOptions = {}): Promise<ApiResponse<T>> {\n const headers: Record<string, string> = {};\n\n if (options.userAgent !== undefined && options.userAgent !== '') {\n headers['User-Agent'] = options.userAgent;\n }\n\n if (options.clientIp !== undefined && options.clientIp !== '') {\n headers['X-Original-Client-Ip'] = options.clientIp;\n }\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/sessions/create',\n body: options.data ?? {},\n headers,\n });\n }\n\n /**\n * Fetches one or more sessions and the events recorded against them.\n *\n * Useful for reconstructing a visitor's journey server-side - which forms they\n * touched, where they stopped. Several identifiers can be fetched in one call,\n * which is cheaper than looping. Timestamps come back in `tz` when you supply\n * one, and in the organisation's default zone otherwise.\n *\n * @param options The session identifiers to fetch, and an optional time zone.\n * @returns The sessions and their recorded events.\n * @throws {NotFoundError} If none of the identifiers resolve.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(options: SessionViewOptions): Promise<ApiResponse<T>> {\n const query: Record<string, string> = {\n session_ids: options.sessions.join(','),\n };\n\n if (options.tz !== undefined && options.tz !== '') {\n query.tz = options.tz;\n }\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/sessions/view',\n query,\n });\n }\n\n /**\n * Updates the attribution and context recorded against an existing session.\n *\n * Call this when information arrives after the session was created - a UTM\n * parameter only present on a later page, a referrer resolved asynchronously.\n * The payload is merged server-side rather than replacing what is already\n * recorded.\n *\n * @param session The session identifier to update.\n * @param data Fields to record. Forwarded unmodified; see the API reference in\n * your AsterMD dashboard for the recognised keys. Accepts the same optional\n * `verification` object documented on {@link Sessions.create}.\n * @returns The updated session.\n * @throws {NotFoundError} If the session does not exist.\n * @throws {ValidationError} If the payload is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async update<T = Record<string, unknown>>(\n session: string,\n data: Record<string, unknown> = {},\n ): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'PUT',\n path: '/sessions/update/{id}',\n pathParams: { id: session },\n body: data,\n });\n }\n\n /**\n * Deletes a session and the events recorded against it.\n *\n * Intended for honouring a visitor's erasure request, not for ordinary\n * cleanup - sessions age out server-side on their own. Deleting one removes the\n * attribution trail for anything that referenced it.\n *\n * @param session The session identifier to delete.\n * @returns The deletion confirmation.\n * @throws {NotFoundError} If the session does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async delete<T = Record<string, unknown>>(session: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'DELETE',\n path: '/sessions/delete/{id}',\n pathParams: { id: session },\n });\n }\n}\n","import type { QueryParams } from '../http/url-builder.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Shipping options - the delivery methods available for an order.\n *\n * Each option carries its name, price, and expected delivery window, which is\n * what a checkout page needs to let a buyer choose. Which options apply can depend\n * on the products in the cart and on the destination, so fetch them at checkout\n * rather than hardcoding a list.\n *\n * Read-only, and configured in your AsterMD dashboard.\n */\nexport class Shippings extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Lists shipping options, filtered and paginated.\n *\n * Pagination details come back in `meta()`. The accepted filters are in the\n * API reference in your AsterMD dashboard.\n *\n * @param query Filters and pagination parameters.\n * @returns The matching shipping options, with pagination in `meta()`.\n * @throws {ValidationError} If a filter is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async list<T = Record<string, unknown>>(query: QueryParams = {}): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/shippings/list',\n query,\n });\n }\n\n /**\n * Fetches one shipping option.\n *\n * @param id The shipping option's id.\n * @returns The shipping option record.\n * @throws {NotFoundError} If the shipping option does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/shippings/view/{id}',\n pathParams: { id },\n });\n }\n}\n","import type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Teleforms - the questionnaire definitions your storefront renders.\n *\n * A teleform is the server-side definition of a form: its fields, their types,\n * their order, and the validation the clinician's workflow expects. Both the\n * pre-qualifying questionnaire and the full intake questionnaire are teleforms.\n * Fetch the definition, render it in your own UI, and submit the answers through\n * {@link IntakeSubmissions} together with the teleform's id.\n *\n * There are two ways in. {@link Teleforms.viewByIdentifier} takes the stable\n * human-readable identifier you configured in your AsterMD dashboard, which is\n * what a storefront normally has hardcoded or in configuration.\n * {@link Teleforms.view} takes the generated id, which is what appears in API\n * responses elsewhere.\n *\n * Definitions change when someone edits the form in the dashboard, so fetch them\n * rather than embedding a copy - but they change rarely, so caching the result\n * for minutes rather than seconds is reasonable.\n */\nexport class Teleforms extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Fetches a teleform definition by its configured identifier.\n *\n * This is the call a storefront usually wants: the identifier is the stable\n * name you chose in your AsterMD dashboard, so it can live in your\n * configuration and survive the form being edited.\n *\n * @param identifier The teleform's configured identifier.\n * @returns The teleform definition.\n * @throws {NotFoundError} If no teleform carries that identifier.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async viewByIdentifier<T = Record<string, unknown>>(identifier: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/teleforms/view-url/{form_json_identifier}',\n pathParams: { form_json_identifier: identifier },\n });\n }\n\n /**\n * Fetches a teleform definition by its generated id.\n *\n * Use this when you already hold an id from another API response - a product's\n * assigned teleform, for instance - rather than a configured identifier.\n *\n * @param id The teleform's generated id.\n * @returns The teleform definition.\n * @throws {NotFoundError} If the teleform does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/teleforms/view/{id}',\n pathParams: { id },\n });\n }\n}\n","import type { QueryParams } from '../http/url-builder.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/** The card network for {@link TreatmentCardOptions.type}. */\nexport type TreatmentCardType = 'amex' | 'visa' | 'mastercard' | 'discover' | 'diners_club' | 'jcb';\n\n/** The settled payment method for {@link TreatmentPaymentOptions.type}. */\nexport type TreatmentPaymentType = 'paypal' | 'apple_pay' | 'gpay' | 'credit_card' | 'pre_paid';\n\n/** The identity check performed for {@link TreatmentIdVerificationOptions.method}. */\nexport type TreatmentIdVerificationMethod = 'ssn' | 'dob' | 'cross_check' | 'document_upload';\n\n/** Card details nested inside {@link TreatmentPaymentOptions.card}. */\nexport interface TreatmentCardOptions {\n /** The card network. */\n type: TreatmentCardType;\n /** The card's last four digits, when available. */\n last_4?: string;\n /** The card's expiry, in the format your integration agreed with AsterMD. */\n exp: string;\n}\n\n/** The settled payment method for {@link Treatments.sync}. */\nexport interface TreatmentPaymentOptions {\n /** How the order was paid for. */\n type: TreatmentPaymentType;\n /** Whether the payment was a pre-authorisation rather than a capture. */\n preAuth: boolean;\n /** Whether the pre-authorisation was a quality-assurance hold rather than a real charge. */\n preAuthQa?: boolean;\n /** The pre-authorised amount, when `preAuth` is true. */\n preAuthAmount?: number;\n /** Card details, when the payment method is card-based. */\n card?: TreatmentCardOptions;\n}\n\n/** The identity check nested inside {@link TreatmentVerificationOptions.id}. */\nexport interface TreatmentIdVerificationOptions {\n /** Whether the check passed. */\n verified: boolean;\n /** Which identity check was performed. */\n method: TreatmentIdVerificationMethod;\n /** The value that was checked, e.g. the SSN or date of birth submitted. */\n value: string;\n}\n\n/** Identity/contact verification already performed by the caller, for {@link Treatments.sync}. */\nexport interface TreatmentVerificationOptions {\n /** Whether the patient's email was verified. */\n email: boolean;\n /** Whether the patient's address was verified. */\n address: boolean;\n /** The identity check performed, if any. */\n id?: TreatmentIdVerificationOptions;\n}\n\n/** Options for {@link Treatments.sync}. */\nexport interface TreatmentSyncOptions {\n /** The session the orders belong to. */\n session: string;\n /** Order identifiers from your own commerce system. */\n orderIds: string[];\n /**\n * The visitor's user agent, forwarded as the required `User-Agent` header.\n * Since the SDK runs server-to-server, only the consuming application knows\n * the real value - read it from the incoming request and pass it here.\n */\n userAgent: string;\n /** Campaign attribution to record alongside the orders. */\n utmSource?: string;\n /** The settled payment method for the order. */\n payment?: TreatmentPaymentOptions;\n /** Identity/contact verification already performed by the caller. */\n verification?: TreatmentVerificationOptions;\n}\n\n/**\n * Treatments - the order, once money has actually changed hands.\n *\n * A treatment is the end of the order flow and the start of the clinical one:\n * the record a clinician reviews, prescribes against, and that fulfilment works\n * from. Create it *after* your own payment flow has settled, never before - the\n * SDK does not process payments, and a treatment recorded against an unsettled\n * payment puts an order into the clinical queue that may never be paid for.\n *\n * There are two ways to record one. {@link Treatments.create} takes the order\n * explicitly, which is what you want when your storefront owns checkout.\n * {@link Treatments.sync} takes a session and a list of order identifiers from\n * your own commerce system, which is what you want when checkout happened\n * elsewhere and you are reconciling after the fact.\n *\n * ```ts\n * await client.treatments().create({\n * external_order_id: 'EXT-123',\n * patient: { id: patientId },\n * product: { id: productId },\n * });\n * ```\n */\nexport class Treatments extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Records a treatment - an order that has been paid for.\n *\n * Call this only after your payment flow has settled. Include your own order\n * identifier as `external_order_id` so the record can be reconciled against\n * your commerce system later.\n *\n * @param data The order: patient, product, your order identifier, and whatever\n * else your configuration requires.\n * @returns The created treatment.\n * @throws {ValidationError} If a field is missing or rejected.\n * @throws {NotFoundError} If the referenced patient or product does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async create<T = Record<string, unknown>>(data: Record<string, unknown>): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/treatments/create',\n body: data,\n });\n }\n\n /**\n * Fetches one treatment, including its clinical status.\n *\n * This is how a storefront shows an order's progress - whether a clinician has\n * reviewed it, whether it has shipped.\n *\n * @param id The treatment's id.\n * @returns The treatment record.\n * @throws {NotFoundError} If the treatment does not exist or is out of scope.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async view<T = Record<string, unknown>>(id: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/treatments/view/{id}',\n pathParams: { id },\n });\n }\n\n /**\n * Lists treatments, filtered and paginated.\n *\n * Useful for an account area showing a patient's order history. Pagination\n * details come back in `meta()`; the accepted filters are in the API reference\n * in your AsterMD dashboard.\n *\n * @param query Filters and pagination parameters.\n * @returns The matching treatments, with pagination in `meta()`.\n * @throws {ValidationError} If a filter is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async list<T = Record<string, unknown>>(query: QueryParams = {}): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'sales',\n method: 'GET',\n path: '/treatments/list',\n query,\n });\n }\n\n /**\n * Reconciles orders placed in your own commerce system against a session.\n *\n * The alternative to {@link Treatments.create} for flows where checkout happens\n * outside your storefront: hand over the session and your order identifiers and\n * the server creates the treatments and attributes them to that session's\n * journey. Several orders can be reconciled in one call. `payment`, when\n * supplied, carries the settled payment method for the order; `verification`,\n * when supplied, records identity/contact verification already performed by\n * the caller.\n *\n * @param options The session, your order identifiers, the required user agent,\n * and optional attribution, payment, and verification details.\n * @returns The created treatments.\n * @throws {TypeError} If `userAgent` is missing or empty.\n * @throws {ValidationError} If the session or an order identifier is rejected.\n * @throws {NotFoundError} If the session does not exist.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async sync<T = Record<string, unknown>>(options: TreatmentSyncOptions): Promise<ApiResponse<T>> {\n if (options.userAgent === undefined || options.userAgent === '') {\n throw new TypeError('Treatments.sync requires a non-empty userAgent.');\n }\n\n const body: Record<string, unknown> = {\n session_id: options.session,\n order_ids: options.orderIds,\n };\n\n if (options.utmSource !== undefined) {\n body.utm_source = options.utmSource;\n }\n\n if (options.payment !== undefined) {\n const payment: Record<string, unknown> = {\n type: options.payment.type,\n pre_auth: options.payment.preAuth,\n };\n\n if (options.payment.preAuthQa !== undefined) {\n payment.pre_auth_qa = options.payment.preAuthQa;\n }\n\n if (options.payment.preAuthAmount !== undefined) {\n payment.pre_auth_amount = options.payment.preAuthAmount;\n }\n\n if (options.payment.card !== undefined) {\n payment.card = options.payment.card;\n }\n\n body.payment = payment;\n }\n\n if (options.verification !== undefined) {\n body.verification = {\n email: options.verification.email,\n address: options.verification.address,\n ...(options.verification.id !== undefined ? { id: options.verification.id } : {}),\n };\n }\n\n return await this.transport.send<T>({\n service: 'sales',\n method: 'POST',\n path: '/treatments/sync',\n body,\n headers: { 'User-Agent': options.userAgent },\n });\n }\n}\n","import type { IdentityCheck } from '../enum/identity-check.js';\nimport type { Transport } from '../http/transport.js';\nimport type { Response as ApiResponse } from '../response.js';\nimport { AbstractResource } from './abstract-resource.js';\n\n/**\n * Verification - checking that the details a prospect typed are real.\n *\n * Telehealth needs more confidence in an address, an email, and an identity than\n * ordinary commerce does: a prescription goes to a physical person at a physical\n * place. These endpoints proxy the provider configured for your organisation, and\n * normalise its answers, so your storefront gets one shape regardless of which\n * provider sits behind it.\n *\n * Address autofill and verification belong at the point of entry - autofill as\n * the buyer types, verification before checkout completes. Email verification\n * catches typos and disposable addresses before they cost you a lost patient.\n * Identity verification is the strongest check and is usually required before a\n * clinician will review a case; which variant applies is carried by\n * {@link IdentityCheck}.\n *\n * Note where these calls appear in debug logs. Redaction covers headers and\n * bodies but never the URL, and the address and email endpoints take their input\n * as a query parameter - so a debug entry for those calls contains the address or\n * email that was checked. That is worth knowing when deciding where debug logs\n * are written and how long they are kept. Identity verification is the exception:\n * it is a POST, and its body is dropped from logs entirely because it carries\n * PII.\n */\nexport class Verification extends AbstractResource {\n /**\n * @param transport The shared transport.\n */\n constructor(transport: Transport) {\n super(transport);\n }\n\n /**\n * Suggests complete addresses for a partial one.\n *\n * Built for a type-ahead: call it as the buyer types and render the\n * suggestions. Debouncing is your side's job - every keystroke here is a round\n * trip and counts against your request allowance.\n *\n * @param search The partial address typed so far.\n * @returns Candidate addresses from the configured provider.\n * @throws {ValidationError} If the search term is rejected.\n * @throws {RateLimitError} If the request allowance is exceeded.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async autofillAddress<T = Record<string, unknown>>(search: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'platform',\n method: 'GET',\n path: '/extensions/address-autofill',\n query: { search },\n });\n }\n\n /**\n * Verifies that an address exists and is deliverable.\n *\n * Call this before checkout completes rather than after: a prescription sent to\n * an undeliverable address is a clinical problem, not just a logistics one. The\n * response carries the provider's normalised form of the address, which is what\n * you should store.\n *\n * @param address The full address to verify.\n * @returns The verification result and the normalised address.\n * @throws {ValidationError} If the address is rejected.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async verifyAddress<T = Record<string, unknown>>(address: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'platform',\n method: 'GET',\n path: '/extensions/address-verify',\n query: { address },\n });\n }\n\n /**\n * Verifies that an email address is real and reachable.\n *\n * Catches typos and disposable addresses at the point of entry. Since every\n * later notification - intake reminders, clinician messages, shipping updates -\n * goes to this address, a bad one silently costs you the patient.\n *\n * @param email The email address to verify.\n * @returns The verification result.\n * @throws {ValidationError} If the address is malformed.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async verifyEmail<T = Record<string, unknown>>(email: string): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'platform',\n method: 'GET',\n path: '/extensions/email-verify',\n query: { email },\n });\n }\n\n /**\n * Runs an identity check against the configured provider.\n *\n * The strongest check available, and usually a precondition for clinical\n * review. Which variant runs is carried by `check`, and each expects a\n * different payload - see the API reference in your AsterMD dashboard for the\n * fields each one needs.\n *\n * The payload carries PII, and a Social Security Number in the `SsnVerify`\n * case. It is never written to a debug log, and it should not be written to\n * yours. The payload cannot override the check you selected.\n *\n * @param check Which identity check to run.\n * @param data The fields that check requires.\n * @returns The verification result.\n * @throws {ValidationError} If the payload does not satisfy the chosen check.\n * @throws {ApiError} On any other non-2xx status.\n * @throws {TransportError} If the request never completed.\n */\n async verifyIdentity<T = Record<string, unknown>>(\n check: IdentityCheck,\n data: Record<string, unknown>,\n ): Promise<ApiResponse<T>> {\n return await this.transport.send<T>({\n service: 'platform',\n method: 'POST',\n path: '/extensions/identity-verify',\n // Data first, slug last: the caller's payload must not rewrite the check.\n body: { ...data, slug: check },\n });\n }\n}\n","import { InMemoryTokenStore } from './auth/in-memory-token-store.js';\nimport { TokenManager } from './auth/token-manager.js';\nimport type { TokenStore } from './auth/token-store.js';\nimport { Config } from './config.js';\nimport { FetchHttpClient } from './http/fetch-http-client.js';\nimport type { HttpClient } from './http/http-client.js';\nimport { LogRedactor } from './http/log-redactor.js';\nimport { LoggingHttpClient, type DebugSink } from './http/logging-http-client.js';\nimport { Transport } from './http/transport.js';\nimport { UrlBuilder } from './http/url-builder.js';\nimport { DailyFileLogSink } from './log/daily-file-log-sink.js';\nimport { Carts } from './resource/carts.js';\nimport { Categories } from './resource/categories.js';\nimport { Channels } from './resource/channels.js';\nimport { CheckoutEvents } from './resource/checkout-events.js';\nimport { Coupons } from './resource/coupons.js';\nimport { DoctorsNetworks } from './resource/doctors-networks.js';\nimport { Geo } from './resource/geo.js';\nimport { IntakeSubmissions } from './resource/intake-submissions.js';\nimport { LabTests } from './resource/lab-tests.js';\nimport { Medications } from './resource/medications.js';\nimport { Opportunities } from './resource/opportunities.js';\nimport { Patients } from './resource/patients.js';\nimport { Products } from './resource/products.js';\nimport { Sessions } from './resource/sessions.js';\nimport { Shippings } from './resource/shippings.js';\nimport { Teleforms } from './resource/teleforms.js';\nimport { Treatments } from './resource/treatments.js';\nimport { Verification } from './resource/verification.js';\n\n/** Options accepted by {@link AsterMDClient}. */\nexport interface AsterMDClientOptions {\n /** OAuth2 client ID issued from your AsterMD dashboard. */\n clientId: string;\n /**\n * OAuth2 client secret. Load it from an environment variable or a secrets\n * manager - never commit it and never ship it to a browser.\n */\n clientSecret: string;\n /** Bare API hostname, no scheme and no path. Defaults to `api.astermd.com`. */\n baseHost?: string;\n /** Client used for every request. Defaults to {@link FetchHttpClient}. */\n httpClient?: HttpClient;\n /** Where the bearer token is cached. Defaults to {@link InMemoryTokenStore}. */\n tokenStore?: TokenStore;\n /** Per-request timeout in seconds. Must be >= 1. Defaults to 10. */\n timeoutSeconds?: number;\n /**\n * Turn on debug logging of every request and response. Requires `debugFile` or\n * `debugSink`. Defaults to `false`.\n */\n debug?: boolean;\n /**\n * Base path for daily log files, e.g. `/var/log/astermd/sdk.log`. Dated\n * filenames are derived from it. Ignored when `debugSink` is given.\n */\n debugFile?: string;\n /**\n * Receives each formatted entry instead of a file. Supplying this makes\n * retention and delivery yours.\n */\n debugSink?: DebugSink;\n /** IANA time zone for log timestamps and daily filenames. Defaults to `UTC`. */\n debugTimezone?: string;\n /**\n * Mask credentials and PHI in debug output. Defaults to `true`. Setting it\n * `false` writes live bearer tokens and the client secret to your sink - never\n * do that in production.\n */\n debugRedact?: boolean;\n /** Days of log files to keep; `0` keeps everything. Defaults to 7. */\n debugRetentionDays?: number;\n}\n\n/**\n * The SDK's entry point: one client, eighteen resources.\n *\n * Construct it once at application start and share it. It holds the credentials,\n * caches the bearer token, and owns the single transport every resource dispatches\n * through, so creating one per request would throw away the token cache and cost a\n * credential exchange each time.\n *\n * ```ts\n * import { AsterMDClient, Event } from '@astermd-hq/sdk';\n *\n * const client = new AsterMDClient({\n * clientId: process.env.ASTERMD_CLIENT_ID!,\n * clientSecret: process.env.ASTERMD_CLIENT_SECRET!,\n * });\n *\n * const { session } = (\n * await client.sessions().create<{ session: string }>()\n * ).data();\n *\n * await client.intakeSubmissions().create({\n * session,\n * event: Event.PreQualifyingInitiated,\n * teleformId: 'your-teleform-id',\n * data: answers,\n * });\n * ```\n *\n * Credentials are issued from your AsterMD dashboard, which is also where the\n * full API reference lives. This client holds a secret, so it belongs on a\n * server: never construct it in code that reaches a browser.\n *\n * Authentication needs no attention from you. The token is acquired lazily on the\n * first call, reused until it is close to expiring, refreshed automatically if the\n * server rejects it, and the organisation it authorises is encoded inside it -\n * which is why no method here takes an organisation argument.\n *\n * Every accessor is memoized, so `client.sessions()` returns the same instance\n * each time and calling it in a hot path costs nothing.\n */\nexport class AsterMDClient {\n readonly #config: Config;\n readonly #transport: Transport;\n\n #sessions?: Sessions;\n #intakeSubmissions?: IntakeSubmissions;\n #carts?: Carts;\n #checkoutEvents?: CheckoutEvents;\n #teleforms?: Teleforms;\n #patients?: Patients;\n #opportunities?: Opportunities;\n #treatments?: Treatments;\n #coupons?: Coupons;\n #doctorsNetworks?: DoctorsNetworks;\n #channels?: Channels;\n #products?: Products;\n #categories?: Categories;\n #labTests?: LabTests;\n #medications?: Medications;\n #shippings?: Shippings;\n #verification?: Verification;\n #geo?: Geo;\n\n /**\n * @param options Credentials, connection settings, and debug configuration.\n * @throws {TypeError} If the credentials are empty, if `baseHost` carries a\n * scheme or path, if `timeoutSeconds` is below 1, or if `debug` is on with\n * neither `debugFile` nor `debugSink`.\n */\n constructor(options: AsterMDClientOptions) {\n const timeoutSeconds = options.timeoutSeconds ?? 10;\n\n this.#config = new Config({\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n baseHost: options.baseHost ?? 'api.astermd.com',\n timeoutSeconds,\n });\n\n let http: HttpClient = options.httpClient ?? new FetchHttpClient(timeoutSeconds);\n\n if (options.debug === true) {\n if (options.debugSink === undefined && options.debugFile === undefined) {\n throw new TypeError('debug mode requires debugFile or debugSink.');\n }\n\n const timeZone = options.debugTimezone ?? 'UTC';\n const sink: DebugSink =\n options.debugSink ??\n ((): DebugSink => {\n const fileSink = new DailyFileLogSink({\n basePath: options.debugFile as string,\n retentionDays: options.debugRetentionDays ?? 7,\n timeZone,\n });\n\n return async (entry: string) => {\n await fileSink.write(entry);\n };\n })();\n\n // Wrap before TokenManager is built, so the credential exchange is logged\n // as well as every resource call.\n http = new LoggingHttpClient({\n inner: http,\n sink,\n timeZone,\n redactor: options.debugRedact === false ? null : new LogRedactor(),\n });\n }\n\n const urlBuilder = new UrlBuilder(this.#config.baseHost());\n\n const tokenManager = new TokenManager({\n config: this.#config,\n httpClient: http,\n urlBuilder,\n store: options.tokenStore ?? new InMemoryTokenStore(),\n });\n\n this.#transport = new Transport({\n httpClient: http,\n urlBuilder,\n tokenProvider: async () => await tokenManager.bearerToken(),\n onUnauthorized: async () => await tokenManager.refresh(),\n });\n }\n\n /**\n * Returns the immutable configuration this client was built with.\n *\n * @returns The configuration, including {@link Config.assetUrl}.\n */\n config(): Config {\n return this.#config;\n }\n\n /**\n * Resolves a relative asset path returned by the API against the AsterMD CDN.\n *\n * A convenience for `client.config().assetUrl(path)`. Treat the result as a\n * fetch URL: download the asset once and serve your own copy rather than\n * hot-linking.\n *\n * @param path Relative asset path as returned by the API.\n * @returns The fully-qualified CDN URL.\n */\n assetUrl(path: string): string {\n return this.#config.assetUrl(path);\n }\n\n /** Sessions - the thread tying one visitor's journey together. */\n sessions(): Sessions {\n return (this.#sessions ??= new Sessions(this.#transport));\n }\n\n /** Intake submissions - pre-qualifying and intake questionnaire answers. */\n intakeSubmissions(): IntakeSubmissions {\n return (this.#intakeSubmissions ??= new IntakeSubmissions(this.#transport));\n }\n\n /** Carts - what the visitor intends to buy. */\n carts(): Carts {\n return (this.#carts ??= new Carts(this.#transport));\n }\n\n /** Checkout events - progress through the checkout funnel. */\n checkoutEvents(): CheckoutEvents {\n return (this.#checkoutEvents ??= new CheckoutEvents(this.#transport));\n }\n\n /** Teleforms - the questionnaire definitions your storefront renders. */\n teleforms(): Teleforms {\n return (this.#teleforms ??= new Teleforms(this.#transport));\n }\n\n /** Patients - the person receiving care. Carries PHI. */\n patients(): Patients {\n return (this.#patients ??= new Patients(this.#transport));\n }\n\n /** Opportunities - a named prospect worth following up. */\n opportunities(): Opportunities {\n return (this.#opportunities ??= new Opportunities(this.#transport));\n }\n\n /** Treatments - the order, once payment has settled. */\n treatments(): Treatments {\n return (this.#treatments ??= new Treatments(this.#transport));\n }\n\n /** Coupons - checking a code the shopper typed against their cart. */\n coupons(): Coupons {\n return (this.#coupons ??= new Coupons(this.#transport));\n }\n\n /** Doctors' networks - handing a case to the clinicians who review it. */\n doctorsNetworks(): DoctorsNetworks {\n return (this.#doctorsNetworks ??= new DoctorsNetworks(this.#transport));\n }\n\n /** Channels - the storefront's own server-side configuration. */\n channels(): Channels {\n return (this.#channels ??= new Channels(this.#transport));\n }\n\n /** Products - the read-only catalogue. */\n products(): Products {\n return (this.#products ??= new Products(this.#transport));\n }\n\n /** Categories - how the catalogue is grouped for browsing. */\n categories(): Categories {\n return (this.#categories ??= new Categories(this.#transport));\n }\n\n /** Lab tests - diagnostic panels a product may require. */\n labTests(): LabTests {\n return (this.#labTests ??= new LabTests(this.#transport));\n }\n\n /** Medications - the reference list backing \"what are you currently taking?\". */\n medications(): Medications {\n return (this.#medications ??= new Medications(this.#transport));\n }\n\n /** Shipping options - delivery methods available for an order. */\n shippings(): Shippings {\n return (this.#shippings ??= new Shippings(this.#transport));\n }\n\n /** Verification - address, email, and identity checks. */\n verification(): Verification {\n return (this.#verification ??= new Verification(this.#transport));\n }\n\n /** Geo - where a visitor is, and whether you may serve them. */\n geo(): Geo {\n return (this.#geo ??= new Geo(this.#transport));\n }\n}\n","/**\n * Journey events recorded alongside intake-submission records.\n *\n * Every call to {@link IntakeSubmissions.create} or\n * {@link IntakeSubmissions.update} must carry one of these events to advance the\n * server-side order-flow state machine.\n *\n * Two separate flows exist, each with three events.\n *\n * Pre-qualifying flow - emitted while the prospect answers eligibility\n * questions:\n * - `PreQualifyingInitiated` → first form step (use with `create()`)\n * - `PreQualifyingInProgress` → intermediate steps (use with `update()`)\n * - `PreQualifyingCompleted` → final step / form submitted (use with `update()`)\n *\n * Intake flow - emitted while the prospect fills in the full intake\n * questionnaire:\n * - `IntakeInitiated` → first form step (use with `create()`)\n * - `IntakeInProgress` → intermediate steps (use with `update()`)\n * - `IntakeCompleted` → final step / form submitted (use with `update()`)\n *\n * In both flows the `*Initiated` event always goes to `create()`, the call that\n * creates the server-side record, and the `*InProgress` / `*Completed` events go\n * to subsequent `update()` calls.\n */\nexport const Event = Object.freeze({\n /** The prospect started the pre-qualifying questionnaire. Pass to `create()`. */\n PreQualifyingInitiated: 'pre_qualifying_initiated',\n /** The prospect advanced through an intermediate pre-qualifying step. Pass to `update()`. */\n PreQualifyingInProgress: 'pre_qualifying_inprogress',\n /** The prospect submitted the pre-qualifying form. Pass to `update()`. */\n PreQualifyingCompleted: 'pre_qualifying_completed',\n /** The prospect started the intake questionnaire. Pass to `create()`. */\n IntakeInitiated: 'intake_initiated',\n /** The prospect advanced through an intermediate intake step. Pass to `update()`. */\n IntakeInProgress: 'intake_inprogress',\n /** The prospect submitted the intake form. Pass to `update()`. */\n IntakeCompleted: 'intake_completed',\n} as const);\n\n/** Any journey event wire value. A bare string literal satisfies this type. */\nexport type Event = (typeof Event)[keyof typeof Event];\n","/**\n * The kind of identity check to run through {@link Verification.verifyIdentity}.\n *\n * Each value selects a different check at the configured provider, and each\n * expects a different payload alongside it - a cross-check of supplied details,\n * a date-of-birth confirmation, or a Social Security Number confirmation. The\n * required fields per check are in the API reference in your AsterMD dashboard.\n *\n * These calls carry PII, and `SsnVerify` in particular. The SDK never writes\n * their request bodies to a debug log.\n */\nexport const IdentityCheck = Object.freeze({\n /** Cross-check the supplied identity details against the provider's records. */\n Crosscheck: 'crosscheck',\n /** Confirm the supplied date of birth. */\n DobVerify: 'dob_verify',\n /** Confirm the supplied Social Security Number. */\n SsnVerify: 'ssn_verify',\n} as const);\n\n/** Any identity-check wire value. A bare string literal satisfies this type. */\nexport type IdentityCheck = (typeof IdentityCheck)[keyof typeof IdentityCheck];\n","import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { Token } from './token.js';\nimport type { TokenStore } from './token-store.js';\n\n/**\n * File-backed {@link TokenStore} that persists the JWT across processes.\n *\n * Unlike {@link InMemoryTokenStore}, this writes the token as JSON to disk so it\n * survives a process boundary. That makes it the right choice where each request\n * or invocation gets a fresh process and an in-memory cache would never be hit,\n * as long as those processes share a filesystem.\n *\n * ```ts\n * const client = new AsterMDClient({\n * clientId: process.env.ASTERMD_CLIENT_ID!,\n * clientSecret: process.env.ASTERMD_CLIENT_SECRET!,\n * tokenStore: new FileTokenStore('/var/cache/astermd/token.json'),\n * });\n * ```\n *\n * The parent directory is created if absent. The file is written `0o600` - owner\n * read and write only - so the token is not world-readable on a shared host. It\n * must be writable by the service account and should sit outside any document\n * root. One file per set of API credentials is enough.\n *\n * Writes are atomic: the content goes to a uniquely named temporary file in the\n * same directory and is then renamed over the target, so a concurrent reader\n * sees either the whole previous token or the whole new one. Both reads and\n * writes treat any failure - missing file, bad permissions, a read-only cache\n * directory, truncated or corrupt JSON - as \"no token\" rather than raising,\n * which makes a damaged cache cost one extra credential exchange instead of\n * breaking the request it was only meant to optimise.\n */\nexport class FileTokenStore implements TokenStore {\n readonly #path: string;\n\n /**\n * @param path Path to the JSON file used for token storage. The parent\n * directory is created on first write if it does not exist.\n */\n constructor(path: string) {\n this.#path = path;\n }\n\n /**\n * Reads the cached token from the file.\n *\n * @returns The stored token, or `null` when the file is absent, unreadable, or\n * does not hold a usable token.\n */\n async get(): Promise<Token | null> {\n let contents: string;\n\n try {\n contents = await readFile(this.#path, 'utf8');\n } catch {\n return null;\n }\n\n if (contents === '') {\n return null;\n }\n\n let decoded: unknown;\n\n try {\n decoded = JSON.parse(contents);\n } catch {\n return null;\n }\n\n if (typeof decoded !== 'object' || decoded === null) {\n return null;\n }\n\n const { value, expires_at: expiresAt } = decoded as Record<string, unknown>;\n\n if (typeof value !== 'string' || typeof expiresAt !== 'string') {\n return null;\n }\n\n const parsed = new Date(expiresAt);\n\n if (Number.isNaN(parsed.getTime())) {\n return null;\n }\n\n return new Token(value, parsed);\n }\n\n /**\n * Writes the token to the backing file, replacing any existing content.\n *\n * Persistence is best-effort: a filesystem failure - a read-only or\n * misconfigured cache directory, a permissions problem - is swallowed rather\n * than thrown, matching the PHP SDK's `put()`, which returns early on a\n * `fopen` failure. This method runs on every token acquisition, so failing it\n * would fail the API call it was only meant to optimise; the cost of a\n * degraded cache is an extra credential exchange next time, not a broken\n * request.\n *\n * @param token The freshly acquired token to persist.\n */\n async put(token: Token): Promise<void> {\n const directory = dirname(this.#path);\n\n // Write then rename: rename is atomic, so a concurrent reader never observes\n // a partially written file. Node has no portable equivalent of PHP's flock.\n const temporary = join(directory, `.${randomUUID()}.tmp`);\n\n try {\n await mkdir(directory, { recursive: true, mode: 0o755 });\n\n const contents = JSON.stringify({\n value: token.value(),\n expires_at: token.expiresAt().toISOString(),\n });\n\n await writeFile(temporary, contents, { mode: 0o600 });\n await rename(temporary, this.#path);\n } catch {\n await rm(temporary, { force: true }).catch(() => undefined);\n }\n }\n\n /**\n * Deletes the backing file so the next {@link FileTokenStore.get} returns\n * `null`.\n *\n * A no-op when the file does not exist, including when another process\n * removed it first. Best-effort otherwise: any other filesystem failure (for\n * example a permissions problem) is swallowed rather than thrown, since a\n * cache that fails to clear only costs a stale-token read on the next call,\n * which {@link FileTokenStore.get} already tolerates.\n */\n async clear(): Promise<void> {\n await rm(this.#path, { force: true }).catch(() => undefined);\n }\n}\n","/** One integration's mapping for a variant or a single-price product. */\nexport interface ChannelProductMapping {\n /** The integration's type, from the mapping's `integration.type`. */\n type: unknown;\n /** Every other key of the raw `mapping` object, verbatim. */\n [key: string]: unknown;\n}\n\n/** A purchasable variant of a product, with its price and integration mapping. */\nexport interface ChannelProductVariant {\n id: unknown;\n name: unknown;\n sku: unknown;\n price: unknown;\n intro_price: unknown;\n sale_price: unknown;\n sale_start: unknown;\n sale_end: unknown;\n mapping: ChannelProductMapping | null;\n}\n\n/** Single-purchase pricing for a product that has no variants. */\nexport interface ChannelProductSingle {\n price: unknown;\n intro_price: unknown;\n sale_price: unknown;\n sale_start: unknown;\n sale_end: unknown;\n mapping: ChannelProductMapping | null;\n}\n\n/** A product as the channel offers it. */\nexport interface ChannelProduct {\n id: unknown;\n name: unknown;\n type: unknown;\n sku: unknown;\n status: unknown;\n visibility: unknown;\n image: unknown;\n description_long: unknown;\n description_short: unknown;\n restrict_multiple: unknown;\n min_buy_qty: unknown;\n max_buy_qty: unknown;\n stock: unknown;\n categories: unknown[];\n condition_treated: unknown[];\n teleforms: unknown;\n labtest: unknown[];\n add_ons: ChannelProduct[];\n single: ChannelProductSingle | null;\n variants: ChannelProductVariant[];\n}\n\n/** The channel's payment processor configuration. */\nexport interface ChannelPaymentProcessor {\n id: unknown;\n name: unknown;\n provider: unknown;\n type: unknown;\n config: unknown;\n}\n\n/** The flattened channel-detail envelope. */\nexport interface ChannelDetailView {\n id: unknown;\n name: unknown;\n description: unknown;\n status: unknown;\n type: unknown;\n auto_inc_id: unknown;\n payment_processor: ChannelPaymentProcessor | null;\n products: ChannelProduct[];\n}\n\n/**\n * Read model over the channel-detail envelope.\n *\n * {@link Channels.details} returns everything a storefront needs in one\n * response, which makes it efficient and awkward in equal measure: the payload is\n * deeply nested, uses the server's own field names, and scatters each variant's\n * integration mapping into a separate parallel list keyed by variant id. Rendering\n * straight from it means writing that reassembly into your view layer.\n *\n * This presenter does the reassembly once. It renames `_id` to `id` throughout,\n * joins each variant to its mapping, hoists single-purchase pricing out of its\n * one-element list, flattens reference objects, and recurses through add-ons so\n * they look like ordinary products:\n *\n * ```ts\n * const detail = ChannelDetail.from((await client.channels().details(channelId)).data());\n *\n * for (const product of detail.products) {\n * for (const variant of product.variants) {\n * render(product.name, variant.name, variant.price, variant.mapping);\n * }\n * }\n * ```\n *\n * Field names in the output stay snake_case, matching the PHP SDK's presenter, so\n * a value produced by one can be compared against the other. Anything the server\n * omitted comes back as `null` rather than `undefined`, and an omitted list comes\n * back empty - so a view can read straight through without optional chaining at\n * every level.\n *\n * The class is not instantiable; it exposes only {@link ChannelDetail.from}.\n */\nexport class ChannelDetail {\n private constructor() {\n // Static-only.\n }\n\n /**\n * Flattens a channel-detail payload into a shape a view can render directly.\n *\n * Pass `response.data()` from {@link Channels.details}. Malformed or missing\n * branches degrade to `null` and `[]` rather than raising, because a channel is\n * configuration a storefront needs at boot: failing the page over one absent\n * optional field would be the wrong trade.\n *\n * @param data The raw `data` object from the channel-detail response.\n * @returns The flattened view.\n */\n static from(data: Record<string, unknown>): ChannelDetailView {\n return {\n id: data._id ?? null,\n name: data.name ?? null,\n description: data.description ?? null,\n status: data.status ?? null,\n type: data.type ?? null,\n auto_inc_id: data.auto_inc_id ?? null,\n payment_processor: processor(data.payment_processor),\n products: toList(data.products).map(entry => product(toObject(entry))),\n };\n }\n}\n\nfunction processor(value: unknown): ChannelPaymentProcessor | null {\n if (typeof value !== 'object' || value === null) {\n return null;\n }\n\n const pp = value as Record<string, unknown>;\n\n return {\n id: pp._id ?? null,\n name: pp.name ?? null,\n provider: pp.provider_category ?? null,\n type: pp.type ?? null,\n config: pp.config ?? null,\n };\n}\n\nfunction product(entry: Record<string, unknown>): ChannelProduct {\n // The server sends variant mappings in a parallel list; index them by variant\n // id so each variant can be joined to its own.\n const byVariant = new Map<string, ChannelProductMapping>();\n\n for (const raw of toList(entry.product_mappings)) {\n const pm = toObject(raw);\n const variantId = pm.variant_id;\n\n if (typeof variantId === 'string') {\n byVariant.set(variantId, mapping(pm));\n }\n }\n\n return coreProduct(toObject(entry.product), byVariant, toList(entry.add_ons));\n}\n\nfunction coreProduct(\n p: Record<string, unknown>,\n byVariant: Map<string, ChannelProductMapping>,\n addOns: unknown[],\n): ChannelProduct {\n const id = typeof p._id === 'string' ? p._id : null;\n\n const variants = toList(p.variants).map(raw => {\n const v = toObject(raw);\n const variantId = typeof v._id === 'string' ? v._id : null;\n\n return variant(v, lookup(byVariant, variantId));\n });\n\n const singleRows = toList(p.single);\n const single = singleRows.length === 0 ? null : singlePricing(toObject(singleRows[0]), lookup(byVariant, id));\n\n return {\n id: p._id ?? null,\n name: p.name ?? null,\n type: p.type ?? null,\n sku: p.sku ?? null,\n status: p.status ?? null,\n visibility: p.visibility ?? null,\n image: p.image ?? null,\n description_long: p.description_long ?? null,\n description_short: p.description_short ?? null,\n restrict_multiple: p.restrict_multiple ?? null,\n min_buy_qty: p.min_buy_qty ?? null,\n max_buy_qty: p.max_buy_qty ?? null,\n stock: p.stock ?? null,\n categories: refs(p.categories),\n condition_treated: refs(p.condition_treated),\n teleforms: refs(p.teleforms)[0] ?? null,\n labtest: refs(p.labtest),\n add_ons: addOns.map(addOn => coreProduct(toObject(addOn), new Map(), [])),\n single,\n variants,\n };\n}\n\nfunction variant(v: Record<string, unknown>, productMapping: ChannelProductMapping | null): ChannelProductVariant {\n return {\n id: v._id ?? null,\n name: v.name ?? null,\n sku: v.sku ?? null,\n price: v.default_price ?? null,\n intro_price: v.intro_price ?? null,\n sale_price: v.sale_price ?? null,\n sale_start: v.sale_start ?? null,\n sale_end: v.sale_end ?? null,\n mapping: productMapping,\n };\n}\n\nfunction singlePricing(\n row: Record<string, unknown>,\n productMapping: ChannelProductMapping | null,\n): ChannelProductSingle {\n return {\n price: row.default_price ?? null,\n intro_price: row.intro_price ?? null,\n sale_price: row.sale_price ?? null,\n sale_start: row.sale_start ?? null,\n sale_end: row.sale_end ?? null,\n mapping: productMapping,\n };\n}\n\nfunction mapping(pm: Record<string, unknown>): ChannelProductMapping {\n const integration = toObject(pm.integration);\n const out: ChannelProductMapping = { type: integration.type ?? null };\n\n for (const [key, value] of Object.entries(toObject(pm.mapping))) {\n if (key !== 'type') {\n out[key] = value;\n }\n }\n\n return out;\n}\n\nfunction refs(list: unknown): unknown[] {\n return toList(list).map(item => ref(item));\n}\n\nfunction ref(r: unknown): unknown {\n if (typeof r !== 'object' || r === null) {\n return r;\n }\n\n const raw = r as Record<string, unknown>;\n const out: Record<string, unknown> = { id: raw._id ?? null };\n\n for (const [key, value] of Object.entries(raw)) {\n if (key !== '_id') {\n out[key] = value;\n }\n }\n\n return out;\n}\n\nfunction lookup(map: Map<string, ChannelProductMapping>, key: string | null): ChannelProductMapping | null {\n return key === null ? null : (map.get(key) ?? null);\n}\n\n/** Coerces a value to a list, treating anything else as empty. */\nfunction toList(value: unknown): unknown[] {\n if (Array.isArray(value)) {\n return value;\n }\n\n // A PHP associative array reaches JSON as an object; take its values, as\n // PHP's array_values would.\n return typeof value === 'object' && value !== null ? Object.values(value) : [];\n}\n\n/** Coerces a value to an object, treating anything else as empty. */\nfunction toObject(value: unknown): Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value) ? (value as Record<string, unknown>) : {};\n}\n","import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';\n\nconst ALGORITHM = 'aes-128-cbc';\nconst IV_LENGTH = 16;\n\n/**\n * Encrypts and decrypts URL query-parameter tokens using AES-128-CBC, with a\n * random 16-byte IV prepended to the ciphertext and the whole thing\n * base64url-encoded.\n *\n * Tracking parameters are encrypted so a browser extension cannot read them in\n * transit. The codec is parameter-agnostic: it carries no knowledge of what it\n * is transporting.\n *\n * Decryption is deliberately total. A malformed, truncated, tampered, or\n * wrongly-keyed token yields an empty object rather than raising, so a storefront\n * can fall back to plain parameters instead of failing a page load:\n *\n * ```ts\n * const params = QueryParamCipher.decrypt(\n * request.query._amd ?? '',\n * process.env.ASTERMD_PARAM_KEY!,\n * );\n * // => { aff_id: '123', utm_source: 'news' } (or {} on any failure)\n * ```\n *\n * The AES key is **never** stored in this SDK. Supply it at every call site from\n * your own configuration - an environment variable, a secrets manager - and keep\n * it in step with whichever service mints the tokens. Contact info@astermd.com to\n * obtain or rotate the key for your organisation.\n *\n * **On integrity:** AES-CBC provides confidentiality but not authentication, so a\n * token cannot be proven unmodified. Treat decrypted parameters as untrusted\n * input and validate them before use.\n *\n * **On the wire format and the PHP SDK:** the encrypted payload is a JSON *list*\n * of `{ key, value }` objects, which is what the PHP SDK writes and reads, so a\n * token minted by either SDK is readable by the other. This class's own surface\n * differs from the PHP one, though: it takes and returns a plain object, since\n * that is what a JS caller wants, and converts at the boundary. A duplicate key\n * on the wire resolves to its last occurrence.\n */\nexport class QueryParamCipher {\n private constructor() {\n // Static-only.\n }\n\n /**\n * Decrypts a query-parameter token into tracking parameters.\n *\n * Call this at your storefront's entry point with the raw token from the URL\n * and the key from your own configuration. Any failure along the way - bad\n * encoding, wrong or malformed key, decryption error, non-list payload -\n * returns an empty object so the caller can fall back to plain parameters.\n *\n * @param token The base64url token, `iv(16) || ciphertext`.\n * @param keyHex 32 hex characters - a 16-byte AES-128 key. Supplied by you;\n * the SDK ships no default.\n * @returns The decrypted parameters, or `{}` on any failure.\n */\n static decrypt(token: string, keyHex: string): Record<string, string> {\n if (token === '' || keyHex === '') {\n return {};\n }\n\n const key = decodeKey(keyHex);\n\n if (key === null) {\n return {};\n }\n\n let blob: Buffer;\n\n try {\n blob = Buffer.from(base64UrlToBase64(token), 'base64');\n } catch {\n return {};\n }\n\n if (blob.byteLength <= IV_LENGTH) {\n return {};\n }\n\n let json: string;\n\n try {\n const decipher = createDecipheriv(ALGORITHM, key, blob.subarray(0, IV_LENGTH));\n json = Buffer.concat([decipher.update(blob.subarray(IV_LENGTH)), decipher.final()]).toString('utf8');\n } catch {\n return {};\n }\n\n let decoded: unknown;\n\n try {\n decoded = JSON.parse(json);\n } catch {\n return {};\n }\n\n if (!Array.isArray(decoded)) {\n return {};\n }\n\n // The payload is [{ key, value }, …]; re-shape defensively and drop entries\n // that carry no usable key.\n const out: Record<string, string> = {};\n\n for (const entry of decoded) {\n if (typeof entry !== 'object' || entry === null) {\n continue;\n }\n\n const { key: name, value } = entry as Record<string, unknown>;\n\n if (!isScalar(name)) {\n continue;\n }\n\n out[phpString(name)] = isScalar(value) ? phpString(value) : '';\n }\n\n return out;\n }\n\n /**\n * Encrypts tracking parameters into a token.\n *\n * Provided mainly for round-trip tests and local tooling. A fresh random IV is\n * generated on every call and prepended to the ciphertext before encoding, so\n * the same parameters never produce the same token twice.\n *\n * Unlike {@link QueryParamCipher.decrypt}, this is strict: a bad key raises\n * rather than being swallowed, because a failed encrypt is a programming error\n * and not untrusted input.\n *\n * @param params The parameters to encrypt. Written to the wire as the\n * `[{ key, value }, …]` list the PHP SDK also reads.\n * @param keyHex 32 hex characters - a 16-byte AES-128 key.\n * @returns The base64url token, `iv(16) || ciphertext`.\n * @throws {TypeError} If `keyHex` is not a valid 16-byte hex key.\n */\n static encrypt(params: Record<string, string>, keyHex: string): string {\n const key = decodeKey(keyHex);\n\n if (key === null) {\n throw new TypeError('keyHex must be 32 hex chars (a 16-byte AES-128 key).');\n }\n\n const payload = JSON.stringify(Object.entries(params).map(([name, value]) => ({ key: name, value })));\n\n const iv = randomBytes(IV_LENGTH);\n const cipher = createCipheriv(ALGORITHM, key, iv);\n const ciphertext = Buffer.concat([cipher.update(payload, 'utf8'), cipher.final()]);\n\n return Buffer.concat([iv, ciphertext]).toString('base64url');\n }\n}\n\n/** Decodes a hex key, returning `null` for anything that is not exactly 16 bytes. */\nfunction decodeKey(keyHex: string): Buffer | null {\n if (!/^[0-9a-fA-F]{32}$/.test(keyHex)) {\n return null;\n }\n\n return Buffer.from(keyHex, 'hex');\n}\n\n/**\n * Restores the standard base64 alphabet and padding.\n *\n * `Buffer.from(s, 'base64')` tolerates the base64url alphabet already, but going\n * through the standard form keeps the behaviour identical to the PHP\n * implementation for inputs that mix alphabets.\n */\nfunction base64UrlToBase64(value: string): string {\n const standard = value.replaceAll('-', '+').replaceAll('_', '/');\n const remainder = standard.length % 4;\n\n return remainder === 0 ? standard : standard + '='.repeat(4 - remainder);\n}\n\nfunction isScalar(value: unknown): value is string | number | boolean {\n return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';\n}\n\n/**\n * Renders a scalar the way PHP's `(string)` cast does, for the case that is\n * actually reachable here.\n *\n * `true` becomes `'1'` and `false` becomes the empty string, where JS's\n * `String()` would give `'true'`/`'false'`. This matters because\n * `QueryParamCipher::encrypt()` in the PHP SDK accepts any scalar, so a token\n * minted there can legitimately carry a JSON boolean - and the two SDKs must\n * render it the same way or they disagree on an untampered token.\n *\n * A fractional-valued float is deliberately **not** special-cased. PHP's\n * `(string)` cast on a float uses `%.*G`-style formatting keyed off the\n * `precision` ini setting (14 significant digits by default, but a deployment\n * can change it), which switches to exponential notation on a magnitude *and*\n * significant-digit threshold that does not line up with JS's own\n * number-to-string conversion - e.g. verified against a live PHP install,\n * `(string) 123456789012345.0` is `'1.2345678901234E+14'` (scientific,\n * despite a magnitude under 1e15) while `(string) 0.0001` stays `'0.0001'`\n * but `(string) 0.00001` already becomes `'1.0E-5'`. Reproducing that\n * precisely would mean re-implementing PHP's `precision`-aware float\n * formatter rather than a one-line fix, so it is left unhandled here - an\n * integer-valued float still round-trips correctly (`String(1)` and PHP's\n * `(string) 1.0` both give `'1'`), and only a genuinely fractional value sent\n * from a PHP encrypter can disagree.\n */\nfunction phpString(value: string | number | boolean): string {\n if (typeof value === 'boolean') {\n return value ? '1' : '';\n }\n\n return String(value);\n}\n"]}
|