@dermacore/node 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,161 @@
1
+ # @dermacore/node
2
+
3
+ Official Node.js SDK for the [Derma Core B2B Partner API](../../docs/api/b2b-partner.openapi.yaml).
4
+ Server-side only — reads your API key from your own backend process, so it
5
+ must never run in a browser bundle.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add @dermacore/node
11
+ ```
12
+
13
+ Requires Node.js ≥ 22 (uses native `fetch`, `FormData`, `Blob`, `AbortSignal.any`/`AbortSignal.timeout`, `crypto.timingSafeEqual` — no runtime dependencies).
14
+
15
+ ## Quick start
16
+
17
+ ```ts
18
+ import { DermaCore } from "@dermacore/node";
19
+ import fs from "node:fs";
20
+
21
+ const dc = new DermaCore({ apiKey: process.env.DERMACORE_API_KEY! });
22
+
23
+ const result = await dc.analysis.submit({
24
+ imageFront: fs.createReadStream("front.jpg"),
25
+ imageLeft: fs.createReadStream("left.jpg"),
26
+ imageRight: fs.createReadStream("right.jpg"),
27
+ patientId: patient.id, // optional — see patients.upsert() below
28
+ });
29
+
30
+ console.log(result.status, result.concerns);
31
+ ```
32
+
33
+ `imageFront`/`imageLeft`/`imageRight`/`imageChin` accept a `Buffer`, `Uint8Array`,
34
+ `Blob`, or a Node `Readable` (e.g. `fs.createReadStream()`) — these are the
35
+ same named-angle fields the API itself defines; passing more than one angle
36
+ enables per-angle heatmaps and improves accuracy over a single frontal shot.
37
+
38
+ ## Configuration
39
+
40
+ ```ts
41
+ new DermaCore({
42
+ apiKey: "sk_live_...", // required
43
+ baseURL: "https://api.dermacore.ai", // default
44
+ timeoutMs: 30_000, // per-request timeout
45
+ maxRetries: 2, // retries on network errors, 429, and 5xx
46
+ defaultHeaders: {}, // sent on every request
47
+ });
48
+ ```
49
+
50
+ ## Error handling
51
+
52
+ Every non-2xx response is thrown as a typed error mapped from the platform's
53
+ `DERMACORE-*` error catalog, so you can `catch` by class instead of parsing
54
+ status codes or `error_code` strings yourself:
55
+
56
+ ```ts
57
+ try {
58
+ await dc.analysis.submit({ imageFront: buf });
59
+ } catch (err) {
60
+ if (err instanceof DermaCore.AuthenticationError) {
61
+ // bad or revoked API key
62
+ } else if (err instanceof DermaCore.RateLimitError) {
63
+ console.log(`retry after ${err.retryAfterSeconds}s`);
64
+ } else if (err instanceof DermaCore.UnprocessableEntityError) {
65
+ // quota exceeded, or a semantically invalid request
66
+ } else if (err instanceof DermaCore.DermaCoreError) {
67
+ console.log(err.code, err.status, err.requestId, err.fieldErrors);
68
+ }
69
+ throw err;
70
+ }
71
+ ```
72
+
73
+ | Class | HTTP status | Notes |
74
+ |---|---|---|
75
+ | `AuthenticationError` | 401 | Missing, invalid, or revoked API key |
76
+ | `PermissionDeniedError` | 403 | Valid key, not authorized for this resource |
77
+ | `NotFoundError` | 404 | |
78
+ | `ConflictError` | 409 | e.g. duplicate patient phone |
79
+ | `PayloadTooLargeError` | 413 | Image > 20 MB, or batch > 50 images |
80
+ | `UnprocessableEntityError` | 422 | Quota exceeded, or invalid request |
81
+ | `RateLimitError` | 429 | `.retryAfterSeconds` from `Retry-After` |
82
+ | `ServiceUnavailableError` | 503 | `.retryAfterSeconds` from `Retry-After` |
83
+ | `InternalServerError` | 5xx | |
84
+ | `APIConnectionError` / `APITimeoutError` | — | No response received |
85
+
86
+ Transient failures (network errors, `429`, `5xx`) are retried automatically
87
+ (`maxRetries`, default 2) with exponential backoff, honoring `Retry-After`
88
+ when the server sends one. `POST /v1/analysis` and `POST /v1/analysis/batch`
89
+ calls get an auto-generated `Idempotency-Key` unless you pass your own, so a
90
+ retried submission never double-charges your quota.
91
+
92
+ ## Batch submission
93
+
94
+ ```ts
95
+ const job = await dc.analysis.batch.submit({
96
+ images: [
97
+ { imageId: "patient-001", data: fs.readFileSync("a.jpg") },
98
+ { imageId: "patient-002", data: fs.readFileSync("b.jpg") },
99
+ ],
100
+ webhookUrl: "https://your-server.com/webhooks/dermacore",
101
+ notifyOnCompletion: true,
102
+ });
103
+
104
+ const status = await dc.analysis.batch.get(job.job_id);
105
+ ```
106
+
107
+ ## Patients
108
+
109
+ ```ts
110
+ const patient = await dc.patients.upsert({
111
+ fullName: "Aisha Al-Rashidi",
112
+ phone: "+966501234567", // upserts by (org, phone) when provided
113
+ });
114
+
115
+ await dc.analysis.submit({ imageFront: buf, patientId: patient.id });
116
+ ```
117
+
118
+ ## Webhooks
119
+
120
+ Verify the `X-DermaCore-Signature` header against the **raw** request body
121
+ before trusting a webhook payload:
122
+
123
+ ```ts
124
+ import { constructEvent, SignatureVerificationError } from "@dermacore/node";
125
+ import express from "express";
126
+
127
+ const app = express();
128
+
129
+ app.post(
130
+ "/webhooks/dermacore",
131
+ express.raw({ type: "application/json" }), // must be the raw body, not JSON-parsed
132
+ (req, res) => {
133
+ try {
134
+ const event = constructEvent(
135
+ req.body,
136
+ req.header("x-dermacore-signature"),
137
+ process.env.DERMACORE_WEBHOOK_SECRET!,
138
+ );
139
+ // handle event.event / event.data
140
+ res.sendStatus(200);
141
+ } catch (err) {
142
+ if (err instanceof SignatureVerificationError) {
143
+ res.status(400).send(`Webhook signature verification failed: ${err.message}`);
144
+ return;
145
+ }
146
+ throw err;
147
+ }
148
+ },
149
+ );
150
+ ```
151
+
152
+ No client instance is needed to verify a webhook — `constructEvent` is a
153
+ standalone function. `dc.webhooks.constructEvent(...)` on a client instance
154
+ does the same thing, for convenience if you already have one around.
155
+
156
+ ## Non-diagnostic disclaimer
157
+
158
+ Results are for wellness and cosmetic guidance only and do not constitute
159
+ medical advice, diagnosis, or treatment — display `disclaimer` from every
160
+ response to your end users. See `docs/decisions/` (ADR set) for the product
161
+ constraints this API is built under.
@@ -0,0 +1,39 @@
1
+ import { type DermaCoreConfig } from "./internal/http.js";
2
+ import { AnalysisResource } from "./resources/analysis.js";
3
+ import { PatientsResource } from "./resources/patients.js";
4
+ import * as errors from "./errors.js";
5
+ export type { DermaCoreConfig } from "./internal/http.js";
6
+ /**
7
+ * Derma Core B2B Partner API client. Server-side only (Node.js) — do not use
8
+ * with a browser bundle, as your API key would be exposed to end users.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const dc = new DermaCore({ apiKey: process.env.DERMACORE_API_KEY! });
13
+ * const result = await dc.analysis.submit({ imageFront: fs.createReadStream("face.jpg") });
14
+ * ```
15
+ */
16
+ export declare class DermaCore {
17
+ readonly analysis: AnalysisResource;
18
+ readonly patients: PatientsResource;
19
+ readonly webhooks: {
20
+ constructEvent: typeof import("./webhooks.js").constructEvent;
21
+ SIGNATURE_HEADER_NAME: string;
22
+ };
23
+ static readonly DermaCoreError: typeof errors.DermaCoreError;
24
+ static readonly APIConnectionError: typeof errors.APIConnectionError;
25
+ static readonly APITimeoutError: typeof errors.APITimeoutError;
26
+ static readonly BadRequestError: typeof errors.BadRequestError;
27
+ static readonly AuthenticationError: typeof errors.AuthenticationError;
28
+ static readonly PermissionDeniedError: typeof errors.PermissionDeniedError;
29
+ static readonly NotFoundError: typeof errors.NotFoundError;
30
+ static readonly ConflictError: typeof errors.ConflictError;
31
+ static readonly PayloadTooLargeError: typeof errors.PayloadTooLargeError;
32
+ static readonly UnprocessableEntityError: typeof errors.UnprocessableEntityError;
33
+ static readonly RateLimitError: typeof errors.RateLimitError;
34
+ static readonly InternalServerError: typeof errors.InternalServerError;
35
+ static readonly ServiceUnavailableError: typeof errors.ServiceUnavailableError;
36
+ static readonly SignatureVerificationError: typeof errors.SignatureVerificationError;
37
+ constructor(config: DermaCoreConfig);
38
+ }
39
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D;;;;;;;;;GASG;AACH,qBAAa,SAAS;IACpB,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,QAAQ;;;MAAqB;IAEtC,MAAM,CAAC,QAAQ,CAAC,cAAc,+BAAyB;IACvD,MAAM,CAAC,QAAQ,CAAC,kBAAkB,mCAA6B;IAC/D,MAAM,CAAC,QAAQ,CAAC,eAAe,gCAA0B;IACzD,MAAM,CAAC,QAAQ,CAAC,eAAe,gCAA0B;IACzD,MAAM,CAAC,QAAQ,CAAC,mBAAmB,oCAA8B;IACjE,MAAM,CAAC,QAAQ,CAAC,qBAAqB,sCAAgC;IACrE,MAAM,CAAC,QAAQ,CAAC,aAAa,8BAAwB;IACrD,MAAM,CAAC,QAAQ,CAAC,aAAa,8BAAwB;IACrD,MAAM,CAAC,QAAQ,CAAC,oBAAoB,qCAA+B;IACnE,MAAM,CAAC,QAAQ,CAAC,wBAAwB,yCAAmC;IAC3E,MAAM,CAAC,QAAQ,CAAC,cAAc,+BAAyB;IACvD,MAAM,CAAC,QAAQ,CAAC,mBAAmB,oCAA8B;IACjE,MAAM,CAAC,QAAQ,CAAC,uBAAuB,wCAAkC;IACzE,MAAM,CAAC,QAAQ,CAAC,0BAA0B,2CAAqC;gBAEnE,MAAM,EAAE,eAAe;CAKpC"}
package/dist/client.js ADDED
@@ -0,0 +1,40 @@
1
+ import { HttpClient } from "./internal/http.js";
2
+ import { AnalysisResource } from "./resources/analysis.js";
3
+ import { PatientsResource } from "./resources/patients.js";
4
+ import { webhooks as webhooksNamespace } from "./webhooks.js";
5
+ import * as errors from "./errors.js";
6
+ /**
7
+ * Derma Core B2B Partner API client. Server-side only (Node.js) — do not use
8
+ * with a browser bundle, as your API key would be exposed to end users.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const dc = new DermaCore({ apiKey: process.env.DERMACORE_API_KEY! });
13
+ * const result = await dc.analysis.submit({ imageFront: fs.createReadStream("face.jpg") });
14
+ * ```
15
+ */
16
+ export class DermaCore {
17
+ analysis;
18
+ patients;
19
+ webhooks = webhooksNamespace;
20
+ static DermaCoreError = errors.DermaCoreError;
21
+ static APIConnectionError = errors.APIConnectionError;
22
+ static APITimeoutError = errors.APITimeoutError;
23
+ static BadRequestError = errors.BadRequestError;
24
+ static AuthenticationError = errors.AuthenticationError;
25
+ static PermissionDeniedError = errors.PermissionDeniedError;
26
+ static NotFoundError = errors.NotFoundError;
27
+ static ConflictError = errors.ConflictError;
28
+ static PayloadTooLargeError = errors.PayloadTooLargeError;
29
+ static UnprocessableEntityError = errors.UnprocessableEntityError;
30
+ static RateLimitError = errors.RateLimitError;
31
+ static InternalServerError = errors.InternalServerError;
32
+ static ServiceUnavailableError = errors.ServiceUnavailableError;
33
+ static SignatureVerificationError = errors.SignatureVerificationError;
34
+ constructor(config) {
35
+ const http = new HttpClient(config);
36
+ this.analysis = new AnalysisResource(http);
37
+ this.patients = new PatientsResource(http);
38
+ }
39
+ }
40
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAwB,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,QAAQ,IAAI,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAItC;;;;;;;;;GASG;AACH,MAAM,OAAO,SAAS;IACX,QAAQ,CAAmB;IAC3B,QAAQ,CAAmB;IAC3B,QAAQ,GAAG,iBAAiB,CAAC;IAEtC,MAAM,CAAU,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC;IACvD,MAAM,CAAU,kBAAkB,GAAG,MAAM,CAAC,kBAAkB,CAAC;IAC/D,MAAM,CAAU,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC;IACzD,MAAM,CAAU,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC;IACzD,MAAM,CAAU,mBAAmB,GAAG,MAAM,CAAC,mBAAmB,CAAC;IACjE,MAAM,CAAU,qBAAqB,GAAG,MAAM,CAAC,qBAAqB,CAAC;IACrE,MAAM,CAAU,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC;IACrD,MAAM,CAAU,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC;IACrD,MAAM,CAAU,oBAAoB,GAAG,MAAM,CAAC,oBAAoB,CAAC;IACnE,MAAM,CAAU,wBAAwB,GAAG,MAAM,CAAC,wBAAwB,CAAC;IAC3E,MAAM,CAAU,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC;IACvD,MAAM,CAAU,mBAAmB,GAAG,MAAM,CAAC,mBAAmB,CAAC;IACjE,MAAM,CAAU,uBAAuB,GAAG,MAAM,CAAC,uBAAuB,CAAC;IACzE,MAAM,CAAU,0BAA0B,GAAG,MAAM,CAAC,0BAA0B,CAAC;IAE/E,YAAY,MAAuB;QACjC,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,CAAC,QAAQ,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC,QAAQ,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Typed error hierarchy mapped 1:1 from the platform's RFC 7807 error
3
+ * responses (`type`, `title`, `status`, `detail`, `instance`, `error_code`,
4
+ * `errors[]`) — see docs/errors/error-catalog.md for the full DERMACORE-*
5
+ * code list. Callers should catch specific subclasses rather than parsing
6
+ * `error_code` strings themselves.
7
+ */
8
+ export interface ApiErrorBody {
9
+ type: string;
10
+ title: string;
11
+ status: number;
12
+ detail: string;
13
+ instance: string;
14
+ error_code: string;
15
+ errors?: Array<{
16
+ field: string;
17
+ message: string;
18
+ }>;
19
+ }
20
+ export interface DermaCoreErrorOptions {
21
+ status: number | undefined;
22
+ code: string | undefined;
23
+ type: string | undefined;
24
+ instance: string | undefined;
25
+ requestId: string | undefined;
26
+ fieldErrors: Array<{
27
+ field: string;
28
+ message: string;
29
+ }> | undefined;
30
+ cause: unknown;
31
+ }
32
+ /** Base class for every error this SDK throws. */
33
+ export declare class DermaCoreError extends Error {
34
+ /** HTTP status code, when the error came from an API response. */
35
+ readonly status: number | undefined;
36
+ /** The `DERMACORE-*` error code from the response body. */
37
+ readonly code: string | undefined;
38
+ /** RFC 7807 `type` URI. */
39
+ readonly type: string | undefined;
40
+ /** RFC 7807 `instance` — the request path that failed. */
41
+ readonly instance: string | undefined;
42
+ /** `X-Request-Id` response header, when present — include this when contacting support. */
43
+ readonly requestId: string | undefined;
44
+ /** Per-field validation errors, present on 400 responses that fail field validation. */
45
+ readonly fieldErrors: Array<{
46
+ field: string;
47
+ message: string;
48
+ }> | undefined;
49
+ constructor(message: string, options?: Partial<DermaCoreErrorOptions>);
50
+ }
51
+ /** Network failure, DNS error, or request timeout — no HTTP response was received. */
52
+ export declare class APIConnectionError extends DermaCoreError {
53
+ }
54
+ /** Request timed out before `timeoutMs` elapsed. */
55
+ export declare class APITimeoutError extends APIConnectionError {
56
+ }
57
+ /** 400 — malformed request or invalid parameters. */
58
+ export declare class BadRequestError extends DermaCoreError {
59
+ }
60
+ /** 401 — missing, invalid, or revoked API key. */
61
+ export declare class AuthenticationError extends DermaCoreError {
62
+ }
63
+ /** 403 — valid API key, but not authorized for this resource. */
64
+ export declare class PermissionDeniedError extends DermaCoreError {
65
+ }
66
+ /** 404 — resource not found, or not accessible by your organization. */
67
+ export declare class NotFoundError extends DermaCoreError {
68
+ }
69
+ /** 409 — conflicting resource state (e.g. duplicate patient phone number). */
70
+ export declare class ConflictError extends DermaCoreError {
71
+ }
72
+ /** 413 — image exceeds 20 MB, or batch exceeds 50 images. */
73
+ export declare class PayloadTooLargeError extends DermaCoreError {
74
+ }
75
+ /** 422 — monthly/daily quota exceeded, or a semantically invalid request. */
76
+ export declare class UnprocessableEntityError extends DermaCoreError {
77
+ }
78
+ /** 429 — rate limit exceeded. */
79
+ export declare class RateLimitError extends DermaCoreError {
80
+ /** Seconds to wait before retrying, from the `Retry-After` header. */
81
+ readonly retryAfterSeconds: number | undefined;
82
+ constructor(message: string, options: Partial<DermaCoreErrorOptions> & {
83
+ retryAfterSeconds: number | undefined;
84
+ });
85
+ }
86
+ /** 5xx — unexpected server error. */
87
+ export declare class InternalServerError extends DermaCoreError {
88
+ }
89
+ /** 503 — analysis pipeline temporarily unavailable. */
90
+ export declare class ServiceUnavailableError extends InternalServerError {
91
+ readonly retryAfterSeconds: number | undefined;
92
+ constructor(message: string, options: Partial<DermaCoreErrorOptions> & {
93
+ retryAfterSeconds: number | undefined;
94
+ });
95
+ }
96
+ /** Thrown by `webhooks.constructEvent()` when the HMAC signature doesn't match. */
97
+ export declare class SignatureVerificationError extends DermaCoreError {
98
+ }
99
+ /**
100
+ * Maps an HTTP status + RFC 7807 body into the matching typed error.
101
+ * `retryAfterSeconds` comes from the `Retry-After` response header, if any.
102
+ */
103
+ export declare function errorFromResponse(status: number, body: Partial<ApiErrorBody> | undefined, context: {
104
+ requestId: string | undefined;
105
+ retryAfterSeconds: number | undefined;
106
+ instance: string;
107
+ }): DermaCoreError;
108
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
package/dist/errors.js ADDED
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Typed error hierarchy mapped 1:1 from the platform's RFC 7807 error
3
+ * responses (`type`, `title`, `status`, `detail`, `instance`, `error_code`,
4
+ * `errors[]`) — see docs/errors/error-catalog.md for the full DERMACORE-*
5
+ * code list. Callers should catch specific subclasses rather than parsing
6
+ * `error_code` strings themselves.
7
+ */
8
+ const defaultOptions = {
9
+ status: undefined,
10
+ code: undefined,
11
+ type: undefined,
12
+ instance: undefined,
13
+ requestId: undefined,
14
+ fieldErrors: undefined,
15
+ cause: undefined,
16
+ };
17
+ /** Base class for every error this SDK throws. */
18
+ export class DermaCoreError extends Error {
19
+ /** HTTP status code, when the error came from an API response. */
20
+ status;
21
+ /** The `DERMACORE-*` error code from the response body. */
22
+ code;
23
+ /** RFC 7807 `type` URI. */
24
+ type;
25
+ /** RFC 7807 `instance` — the request path that failed. */
26
+ instance;
27
+ /** `X-Request-Id` response header, when present — include this when contacting support. */
28
+ requestId;
29
+ /** Per-field validation errors, present on 400 responses that fail field validation. */
30
+ fieldErrors;
31
+ constructor(message, options = {}) {
32
+ const opts = { ...defaultOptions, ...options };
33
+ super(message, opts.cause !== undefined ? { cause: opts.cause } : undefined);
34
+ this.name = new.target.name;
35
+ this.status = opts.status;
36
+ this.code = opts.code;
37
+ this.type = opts.type;
38
+ this.instance = opts.instance;
39
+ this.requestId = opts.requestId;
40
+ this.fieldErrors = opts.fieldErrors;
41
+ }
42
+ }
43
+ /** Network failure, DNS error, or request timeout — no HTTP response was received. */
44
+ export class APIConnectionError extends DermaCoreError {
45
+ }
46
+ /** Request timed out before `timeoutMs` elapsed. */
47
+ export class APITimeoutError extends APIConnectionError {
48
+ }
49
+ /** 400 — malformed request or invalid parameters. */
50
+ export class BadRequestError extends DermaCoreError {
51
+ }
52
+ /** 401 — missing, invalid, or revoked API key. */
53
+ export class AuthenticationError extends DermaCoreError {
54
+ }
55
+ /** 403 — valid API key, but not authorized for this resource. */
56
+ export class PermissionDeniedError extends DermaCoreError {
57
+ }
58
+ /** 404 — resource not found, or not accessible by your organization. */
59
+ export class NotFoundError extends DermaCoreError {
60
+ }
61
+ /** 409 — conflicting resource state (e.g. duplicate patient phone number). */
62
+ export class ConflictError extends DermaCoreError {
63
+ }
64
+ /** 413 — image exceeds 20 MB, or batch exceeds 50 images. */
65
+ export class PayloadTooLargeError extends DermaCoreError {
66
+ }
67
+ /** 422 — monthly/daily quota exceeded, or a semantically invalid request. */
68
+ export class UnprocessableEntityError extends DermaCoreError {
69
+ }
70
+ /** 429 — rate limit exceeded. */
71
+ export class RateLimitError extends DermaCoreError {
72
+ /** Seconds to wait before retrying, from the `Retry-After` header. */
73
+ retryAfterSeconds;
74
+ constructor(message, options) {
75
+ super(message, options);
76
+ this.retryAfterSeconds = options.retryAfterSeconds;
77
+ }
78
+ }
79
+ /** 5xx — unexpected server error. */
80
+ export class InternalServerError extends DermaCoreError {
81
+ }
82
+ /** 503 — analysis pipeline temporarily unavailable. */
83
+ export class ServiceUnavailableError extends InternalServerError {
84
+ retryAfterSeconds;
85
+ constructor(message, options) {
86
+ super(message, options);
87
+ this.retryAfterSeconds = options.retryAfterSeconds;
88
+ }
89
+ }
90
+ /** Thrown by `webhooks.constructEvent()` when the HMAC signature doesn't match. */
91
+ export class SignatureVerificationError extends DermaCoreError {
92
+ }
93
+ /**
94
+ * Maps an HTTP status + RFC 7807 body into the matching typed error.
95
+ * `retryAfterSeconds` comes from the `Retry-After` response header, if any.
96
+ */
97
+ export function errorFromResponse(status, body, context) {
98
+ const message = body?.detail ?? `Request failed with status ${status}`;
99
+ const base = {
100
+ status,
101
+ code: body?.error_code,
102
+ type: body?.type,
103
+ instance: body?.instance ?? context.instance,
104
+ requestId: context.requestId,
105
+ fieldErrors: body?.errors,
106
+ };
107
+ switch (status) {
108
+ case 400:
109
+ return new BadRequestError(message, base);
110
+ case 401:
111
+ return new AuthenticationError(message, base);
112
+ case 403:
113
+ return new PermissionDeniedError(message, base);
114
+ case 404:
115
+ return new NotFoundError(message, base);
116
+ case 409:
117
+ return new ConflictError(message, base);
118
+ case 413:
119
+ return new PayloadTooLargeError(message, base);
120
+ case 422:
121
+ return new UnprocessableEntityError(message, base);
122
+ case 429:
123
+ return new RateLimitError(message, { ...base, retryAfterSeconds: context.retryAfterSeconds });
124
+ case 503:
125
+ return new ServiceUnavailableError(message, { ...base, retryAfterSeconds: context.retryAfterSeconds });
126
+ default:
127
+ if (status >= 500)
128
+ return new InternalServerError(message, base);
129
+ return new DermaCoreError(message, base);
130
+ }
131
+ }
132
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { DermaCore } from "./client.js";
2
+ export { DermaCore };
3
+ export default DermaCore;
4
+ export type { DermaCoreConfig } from "./internal/http.js";
5
+ export type { RateLimitInfo } from "./internal/http.js";
6
+ export type { BinaryInput } from "./internal/binary.js";
7
+ export * from "./errors.js";
8
+ export * from "./types.js";
9
+ export { constructEvent, SIGNATURE_HEADER_NAME } from "./webhooks.js";
10
+ export type { WebhookEvent, ConstructEventOptions } from "./webhooks.js";
11
+ export type { SubmitAnalysisParams, ListAnalysesParams, UpdateAnalysisMetadataParams, AnalysisMetadataResult, SubmitBatchAnalysisParams, BatchImageItem, } from "./resources/analysis.js";
12
+ export type { UpsertPatientParams, SearchPatientsParams, ListPatientsParams, PatientListResponse, } from "./resources/patients.js";
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,SAAS,EAAE,CAAC;AACrB,eAAe,SAAS,CAAC;AAEzB,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAE3B,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtE,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEzE,YAAY,EACV,oBAAoB,EACpB,kBAAkB,EAClB,4BAA4B,EAC5B,sBAAsB,EACtB,yBAAyB,EACzB,cAAc,GACf,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import { DermaCore } from "./client.js";
2
+ export { DermaCore };
3
+ export default DermaCore;
4
+ export * from "./errors.js";
5
+ export * from "./types.js";
6
+ export { constructEvent, SIGNATURE_HEADER_NAME } from "./webhooks.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,SAAS,EAAE,CAAC;AACrB,eAAe,SAAS,CAAC;AAMzB,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAE3B,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { Readable } from "node:stream";
2
+ /**
3
+ * Anything an image field can accept. `Readable` covers `fs.createReadStream()`,
4
+ * the common case for a partner backend reading a file from disk.
5
+ */
6
+ export type BinaryInput = Buffer | Uint8Array | Blob | Readable;
7
+ export declare function toBlob(input: BinaryInput, contentType?: string): Promise<Blob>;
8
+ export declare function toBase64DataUrl(input: BinaryInput, mimeType: string): Promise<string>;
9
+ //# sourceMappingURL=binary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binary.d.ts","sourceRoot":"","sources":["../../src/internal/binary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,IAAI,GAAG,QAAQ,CAAC;AAEhE,wBAAsB,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQpF;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAU3F"}
@@ -0,0 +1,26 @@
1
+ import { Readable } from "node:stream";
2
+ import { buffer as streamToBuffer } from "node:stream/consumers";
3
+ export async function toBlob(input, contentType) {
4
+ const type = contentType ?? "application/octet-stream";
5
+ if (input instanceof Blob)
6
+ return input;
7
+ if (input instanceof Readable) {
8
+ const buf = await streamToBuffer(input);
9
+ return new Blob([buf], { type });
10
+ }
11
+ return new Blob([input], { type });
12
+ }
13
+ export async function toBase64DataUrl(input, mimeType) {
14
+ let buf;
15
+ if (input instanceof Readable) {
16
+ buf = await streamToBuffer(input);
17
+ }
18
+ else if (input instanceof Blob) {
19
+ buf = Buffer.from(await input.arrayBuffer());
20
+ }
21
+ else {
22
+ buf = Buffer.isBuffer(input) ? input : Buffer.from(input);
23
+ }
24
+ return `data:${mimeType};base64,${buf.toString("base64")}`;
25
+ }
26
+ //# sourceMappingURL=binary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binary.js","sourceRoot":"","sources":["../../src/internal/binary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,MAAM,IAAI,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAQjE,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,KAAkB,EAAE,WAAoB;IACnE,MAAM,IAAI,GAAG,WAAW,IAAI,0BAA0B,CAAC;IACvD,IAAI,KAAK,YAAY,IAAI;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,CAAC;QACxC,OAAO,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAkB,EAAE,QAAgB;IACxE,IAAI,GAAW,CAAC;IAChB,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,GAAG,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;SAAM,IAAI,KAAK,YAAY,IAAI,EAAE,CAAC;QACjC,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;IAC/C,CAAC;SAAM,CAAC;QACN,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,QAAQ,QAAQ,WAAW,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,39 @@
1
+ export interface DermaCoreConfig {
2
+ /** Your Derma Core API key (`sk_live_...` / `sk_test_...`). */
3
+ apiKey: string;
4
+ /** Default: `https://api.dermacore.ai`. */
5
+ baseURL?: string;
6
+ /** Per-request timeout, in milliseconds. Default: `30000`. */
7
+ timeoutMs?: number;
8
+ /** Retries for network errors, `429`, and `5xx` responses. Default: `2`. */
9
+ maxRetries?: number;
10
+ /** Headers sent on every request. */
11
+ defaultHeaders?: Record<string, string>;
12
+ /** Override for testing, or to use an undici `Agent`-bound fetch. */
13
+ fetch?: typeof fetch;
14
+ }
15
+ export type QueryValue = string | number | boolean | undefined;
16
+ export interface RequestOptions {
17
+ query?: Record<string, QueryValue>;
18
+ json?: unknown;
19
+ form?: FormData;
20
+ idempotencyKey?: string;
21
+ headers?: Record<string, string>;
22
+ signal?: AbortSignal;
23
+ }
24
+ export interface RateLimitInfo {
25
+ limit: number | undefined;
26
+ remaining: number | undefined;
27
+ resetSeconds: number | undefined;
28
+ }
29
+ export interface ApiResponse<T> {
30
+ data: T;
31
+ requestId: string | undefined;
32
+ rateLimit: RateLimitInfo;
33
+ }
34
+ export declare class HttpClient {
35
+ #private;
36
+ constructor(config: DermaCoreConfig);
37
+ request<T>(method: string, path: string, options?: RequestOptions): Promise<ApiResponse<T>>;
38
+ }
39
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/internal/http.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,qEAAqE;IACrE,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAWD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAE/D,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,IAAI,EAAE,CAAC,CAAC;IACR,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,EAAE,aAAa,CAAC;CAC1B;AAED,qBAAa,UAAU;;gBAGT,MAAM,EAAE,eAAe;IAc7B,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;CAsFtG"}