@attestry/sdk 0.6.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.
Files changed (99) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +1269 -0
  3. package/dist/client.d.ts +58 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +74 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/constants.d.ts +7 -0
  8. package/dist/constants.d.ts.map +1 -0
  9. package/dist/constants.js +43 -0
  10. package/dist/constants.js.map +1 -0
  11. package/dist/errors.d.ts +16 -0
  12. package/dist/errors.d.ts.map +1 -0
  13. package/dist/errors.js +41 -0
  14. package/dist/errors.js.map +1 -0
  15. package/dist/index.d.ts +17 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +20 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/lines-parser.d.ts +50 -0
  20. package/dist/lines-parser.d.ts.map +1 -0
  21. package/dist/lines-parser.js +211 -0
  22. package/dist/lines-parser.js.map +1 -0
  23. package/dist/ndjson-parser.d.ts +57 -0
  24. package/dist/ndjson-parser.d.ts.map +1 -0
  25. package/dist/ndjson-parser.js +245 -0
  26. package/dist/ndjson-parser.js.map +1 -0
  27. package/dist/resources/abac-policies.d.ts +1034 -0
  28. package/dist/resources/abac-policies.d.ts.map +1 -0
  29. package/dist/resources/abac-policies.js +1519 -0
  30. package/dist/resources/abac-policies.js.map +1 -0
  31. package/dist/resources/audit-log.d.ts +588 -0
  32. package/dist/resources/audit-log.d.ts.map +1 -0
  33. package/dist/resources/audit-log.js +629 -0
  34. package/dist/resources/audit-log.js.map +1 -0
  35. package/dist/resources/batch.d.ts +845 -0
  36. package/dist/resources/batch.d.ts.map +1 -0
  37. package/dist/resources/batch.js +1074 -0
  38. package/dist/resources/batch.js.map +1 -0
  39. package/dist/resources/chat.d.ts +151 -0
  40. package/dist/resources/chat.d.ts.map +1 -0
  41. package/dist/resources/chat.js +124 -0
  42. package/dist/resources/chat.js.map +1 -0
  43. package/dist/resources/check.d.ts +348 -0
  44. package/dist/resources/check.d.ts.map +1 -0
  45. package/dist/resources/check.js +543 -0
  46. package/dist/resources/check.js.map +1 -0
  47. package/dist/resources/compliance-check.d.ts +330 -0
  48. package/dist/resources/compliance-check.d.ts.map +1 -0
  49. package/dist/resources/compliance-check.js +402 -0
  50. package/dist/resources/compliance-check.js.map +1 -0
  51. package/dist/resources/decisions.d.ts +1208 -0
  52. package/dist/resources/decisions.d.ts.map +1 -0
  53. package/dist/resources/decisions.js +1362 -0
  54. package/dist/resources/decisions.js.map +1 -0
  55. package/dist/resources/evidence-pack.d.ts +1080 -0
  56. package/dist/resources/evidence-pack.d.ts.map +1 -0
  57. package/dist/resources/evidence-pack.js +1789 -0
  58. package/dist/resources/evidence-pack.js.map +1 -0
  59. package/dist/resources/gate.d.ts +613 -0
  60. package/dist/resources/gate.d.ts.map +1 -0
  61. package/dist/resources/gate.js +737 -0
  62. package/dist/resources/gate.js.map +1 -0
  63. package/dist/resources/incidents.d.ts +136 -0
  64. package/dist/resources/incidents.d.ts.map +1 -0
  65. package/dist/resources/incidents.js +229 -0
  66. package/dist/resources/incidents.js.map +1 -0
  67. package/dist/resources/regulatory-changes.d.ts +307 -0
  68. package/dist/resources/regulatory-changes.d.ts.map +1 -0
  69. package/dist/resources/regulatory-changes.js +365 -0
  70. package/dist/resources/regulatory-changes.js.map +1 -0
  71. package/dist/resources/safe-input-read.d.ts +21 -0
  72. package/dist/resources/safe-input-read.d.ts.map +1 -0
  73. package/dist/resources/safe-input-read.js +57 -0
  74. package/dist/resources/safe-input-read.js.map +1 -0
  75. package/dist/resources/ship-gate.d.ts +475 -0
  76. package/dist/resources/ship-gate.d.ts.map +1 -0
  77. package/dist/resources/ship-gate.js +727 -0
  78. package/dist/resources/ship-gate.js.map +1 -0
  79. package/dist/resources/vision.d.ts +540 -0
  80. package/dist/resources/vision.d.ts.map +1 -0
  81. package/dist/resources/vision.js +1036 -0
  82. package/dist/resources/vision.js.map +1 -0
  83. package/dist/retry.d.ts +103 -0
  84. package/dist/retry.d.ts.map +1 -0
  85. package/dist/retry.js +224 -0
  86. package/dist/retry.js.map +1 -0
  87. package/dist/sse-parser.d.ts +64 -0
  88. package/dist/sse-parser.d.ts.map +1 -0
  89. package/dist/sse-parser.js +271 -0
  90. package/dist/sse-parser.js.map +1 -0
  91. package/dist/transport.d.ts +142 -0
  92. package/dist/transport.d.ts.map +1 -0
  93. package/dist/transport.js +455 -0
  94. package/dist/transport.js.map +1 -0
  95. package/dist/types.d.ts +61 -0
  96. package/dist/types.d.ts.map +1 -0
  97. package/dist/types.js +3 -0
  98. package/dist/types.js.map +1 -0
  99. package/package.json +44 -0
@@ -0,0 +1,455 @@
1
+ // ─── HTTP transport ─────────────────────────────────────────────────────────
2
+ //
3
+ // `request` is the single-entry point for every API call. Resources call
4
+ // it via the client. Behavior:
5
+ //
6
+ // - Adds `x-api-key` header from the client's options.
7
+ // - Adds `Content-Type: application/json` when sending a body.
8
+ // - Adds `Accept: application/json` always.
9
+ // - Composes the final URL from `baseUrl + path` with safe slash handling.
10
+ // - Times out via `AbortController` after `timeoutMs` (default 30s);
11
+ // caller can also pass their own `signal` which is composed with the
12
+ // timeout signal.
13
+ // - Decodes 2xx JSON responses to `T`. 204 / empty body returns `null
14
+ // as T` — caller types tell the truth.
15
+ // - Unwraps the kernel's `successResponse` envelope (`{success:true, data}`)
16
+ // when present; bare bodies pass through.
17
+ // - Throws `AttestryAPIError` for any non-2xx status with the parsed
18
+ // body (or `null` if the body wasn't JSON / was empty).
19
+ // - Throws `AttestryError` for fetch failures (network down, DNS fail,
20
+ // timeout). The original error is attached as `cause`.
21
+ // - Automatically retries on HTTP 429 (default 3 retries, exponential
22
+ // backoff with full jitter, honors `Retry-After` header). Configurable
23
+ // via `AttestryClientOptions.retry` and `RequestOptions.retry`. See
24
+ // `retry.ts` and `audit-prompt-F.1-retry-middleware.md` for design.
25
+ // Retry on other transient statuses (5xx) is NOT implemented — adding
26
+ // it requires HTTP-level idempotency-key support on the kernel side
27
+ // to safely re-send POSTs.
28
+ import { AttestryAPIError, AttestryError } from "./errors.js";
29
+ import { attachRetryAfter, isRetryableError, computeRetryDelay, resolveRetryOptions, sleepWithSignal, } from "./retry.js";
30
+ const DEFAULT_BASE_URL = "https://app.attestry.ai";
31
+ const DEFAULT_TIMEOUT_MS = 30_000;
32
+ /**
33
+ * Validates options at construction time. Surfaces every misconfiguration
34
+ * as `AttestryError` BEFORE any request is made — fail-fast beats failing
35
+ * deep in the resource methods. Used by `AttestryClient` constructor.
36
+ */
37
+ export function resolveClientConfig(options) {
38
+ if (typeof options.apiKey !== "string" || options.apiKey.length === 0) {
39
+ throw new AttestryError("AttestryClient: `apiKey` is required");
40
+ }
41
+ const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
42
+ if (baseUrl.length === 0) {
43
+ throw new AttestryError("AttestryClient: `baseUrl` cannot be empty");
44
+ }
45
+ const fetchImpl = options.fetch ?? globalThis.fetch;
46
+ if (typeof fetchImpl !== "function") {
47
+ throw new AttestryError("AttestryClient: no `fetch` implementation available — pass `options.fetch` or run on Node 18+");
48
+ }
49
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
50
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
51
+ throw new AttestryError("AttestryClient: `timeoutMs` must be a non-negative finite number");
52
+ }
53
+ // Validate retry option at construction time too — `resolveRetryOptions`
54
+ // throws on invalid values (negative maxRetries, non-finite delay, etc.).
55
+ // Calling it here means a misconfigured client surfaces fast, not on
56
+ // first request. We discard the result; per-call `resolveRetryOptions`
57
+ // re-merges with any RequestOptions.retry override.
58
+ if (options.retry !== undefined) {
59
+ resolveRetryOptions(options.retry, undefined);
60
+ }
61
+ return {
62
+ apiKey: options.apiKey,
63
+ baseUrl,
64
+ fetchImpl,
65
+ timeoutMs,
66
+ retry: options.retry,
67
+ };
68
+ }
69
+ /**
70
+ * Internal — exported for tests. Composes a URL safely from baseUrl + path.
71
+ * Caller-supplied `path` may or may not start with `/`; either way works.
72
+ */
73
+ export function composeUrl(baseUrl, path) {
74
+ const sep = path.startsWith("/") ? "" : "/";
75
+ return baseUrl + sep + path;
76
+ }
77
+ /**
78
+ * Public request runner. Wraps the single-attempt `requestOnce` with the
79
+ * retry loop. Resources should NOT import this directly — they go
80
+ * through `AttestryClient._request`.
81
+ *
82
+ * Retry behavior: on 429 only (today). The retry loop honors the caller's
83
+ * abort signal — `signal.abort()` during the backoff sleep rejects with
84
+ * `AttestryError("request aborted by caller")` rather than completing
85
+ * the wait.
86
+ */
87
+ export async function request(args) {
88
+ const retry = resolveRetryOptions(args.config.retry, args.options?.retry);
89
+ const callerSignal = args.options?.signal;
90
+ let attempt = 0;
91
+ // Loop bounded by retry.maxRetries (validated 0-100 at construction).
92
+ // No-retry case: max=0, body runs once, errors throw out of the catch.
93
+ while (true) {
94
+ try {
95
+ return await requestOnce(args);
96
+ }
97
+ catch (err) {
98
+ if (isRetryableError(err) &&
99
+ attempt < retry.maxRetries) {
100
+ const delay = computeRetryDelay(err, attempt, retry);
101
+ await sleepWithSignal(delay, callerSignal);
102
+ attempt++;
103
+ continue;
104
+ }
105
+ throw err;
106
+ }
107
+ }
108
+ }
109
+ /**
110
+ * Internal single-attempt request runner. Exposed under `__test__` for
111
+ * unit pinning.
112
+ */
113
+ export async function requestOnce(args) {
114
+ const { config, method, path, body, query, options } = args;
115
+ const callerSignal = options?.signal;
116
+ // Reject upfront on a pre-aborted caller signal. Real `fetch` rejects
117
+ // synchronously in this case but mock / alternative fetch impls often
118
+ // do not — making this explicit keeps the SDK contract uniform across
119
+ // transports.
120
+ if (callerSignal?.aborted) {
121
+ throw new AttestryError("request aborted by caller", {
122
+ cause: callerSignal.reason,
123
+ });
124
+ }
125
+ const queryString = encodeQuery(query);
126
+ const url = composeUrl(config.baseUrl, path) + queryString;
127
+ const headers = new Headers();
128
+ headers.set("x-api-key", config.apiKey);
129
+ headers.set("Accept", "application/json");
130
+ if (body !== undefined) {
131
+ headers.set("Content-Type", "application/json");
132
+ }
133
+ const abort = new AbortController();
134
+ const timeoutHandle = config.timeoutMs > 0
135
+ ? setTimeout(() => abort.abort(new Error("request timed out")), config.timeoutMs)
136
+ : null;
137
+ // Compose caller signal with our timeout signal: abort if either fires.
138
+ const onCallerAbort = () => abort.abort(callerSignal?.reason);
139
+ if (callerSignal) {
140
+ callerSignal.addEventListener("abort", onCallerAbort, { once: true });
141
+ }
142
+ // Serialize the body up front so a circular-ref / BigInt throw doesn't
143
+ // get masked as "network error" by the catch around fetchImpl. Hostile-
144
+ // review L2.
145
+ let serializedBody;
146
+ if (body !== undefined) {
147
+ try {
148
+ serializedBody = JSON.stringify(body);
149
+ }
150
+ catch (err) {
151
+ throw new AttestryError(
152
+ // JSON.stringify throws TypeError (Error subclass) for circular
153
+ // refs / BigInts, so the String(err) branch is unreachable.
154
+ // Defense-in-depth marker for the v8 coverage tool.
155
+ /* v8 ignore next */
156
+ `invalid request body: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
157
+ }
158
+ }
159
+ let response;
160
+ try {
161
+ response = await config.fetchImpl(url, {
162
+ method,
163
+ headers,
164
+ body: serializedBody,
165
+ signal: abort.signal,
166
+ });
167
+ }
168
+ catch (err) {
169
+ throw new AttestryError(describeFetchFailure(err, abort.signal.aborted, callerSignal?.aborted ?? false), { cause: err });
170
+ }
171
+ finally {
172
+ // Single cleanup site — finally always runs even after a throw in
173
+ // catch, so the prior catch-block duplicates were dead code.
174
+ if (timeoutHandle !== null)
175
+ clearTimeout(timeoutHandle);
176
+ if (callerSignal)
177
+ callerSignal.removeEventListener("abort", onCallerAbort);
178
+ }
179
+ if (response.ok) {
180
+ // P3 hardening: content-type guard for sync GET. Symmetric with
181
+ // streamRequest's expectedContentType guard. Skipped for 204 (No
182
+ // Content) — has no body to validate. (304 Not Modified is also
183
+ // bodyless but `response.ok` is false for 304 (status range
184
+ // 200-299), so it never reaches this branch — handled by the
185
+ // error path instead.) Without this guard, a proxy / LB that
186
+ // wraps a 200 OK around an HTML error page would silently
187
+ // produce `null` (JSON.parse fails → readBody returns parsed:null
188
+ // → consumer gets null cast as T). Pre-P3 G6 documented this
189
+ // soft-fail surface; P3 replaces it with a clear AttestryAPIError
190
+ // naming the expected vs actual content-type.
191
+ if (response.status !== 204) {
192
+ const expectedContentType = args.expectedContentType ?? "application/json";
193
+ // `?? ""` defends against a null Content-Type header — defense-in-
194
+ // depth (the kernel always sets it, so the null branch is exotic).
195
+ /* v8 ignore next */
196
+ const contentType = response.headers.get("content-type") ?? "";
197
+ const semicolonIdx = contentType.indexOf(";");
198
+ const contentTypeMime = (semicolonIdx === -1 ? contentType : contentType.slice(0, semicolonIdx))
199
+ .trim()
200
+ .toLowerCase();
201
+ const expectedMime = expectedContentType.trim().toLowerCase();
202
+ if (contentTypeMime !== expectedMime) {
203
+ throw new AttestryAPIError(`expected ${expectedContentType} response, got "${contentType}"`, response.status, null);
204
+ }
205
+ }
206
+ const { parsed } = await readBody(response);
207
+ // Kernel routes use successResponse() which emits {success:true, data}.
208
+ // Unwrap when the envelope is present; pass through bare for forward-
209
+ // compat with hypothetical non-conforming endpoints (none currently
210
+ // exist). 204 / empty 200 keeps returning null — readBody already
211
+ // short-circuits parsed:null in that case.
212
+ if (parsed !== null &&
213
+ typeof parsed === "object" &&
214
+ parsed.success === true &&
215
+ "data" in parsed) {
216
+ return parsed.data;
217
+ }
218
+ return parsed;
219
+ }
220
+ // Hostile-review H1: non-JSON error bodies (LB 502 HTML, plain-text
221
+ // proxy errors) used to surface as `details: null`, leaving consumers
222
+ // no way to debug. Fall back to the raw text when the body can't be
223
+ // parsed as JSON.
224
+ const { parsed, raw } = await readBody(response);
225
+ const detail = parsed ?? (raw.length > 0 ? raw : null);
226
+ const apiErr = new AttestryAPIError(extractMessage(detail) ?? `Attestry API returned ${response.status}`, response.status, detail);
227
+ // Attach Retry-After header (if any) for the retry middleware. Stored
228
+ // as a non-enumerable property — doesn't pollute JSON.stringify or
229
+ // user-facing console.log of the error.
230
+ attachRetryAfter(apiErr, response.headers.get("retry-after"));
231
+ throw apiErr;
232
+ }
233
+ /**
234
+ * Streaming-response request — used for any long-lived streaming endpoint
235
+ * (SSE, NDJSON). Mirrors `request` for header / signal / URL composition
236
+ * but:
237
+ *
238
+ * - Hardcodes `method: "GET"` (streaming endpoints today are read-only).
239
+ * - Sends `Accept: <expectedContentType>` (default `text/event-stream`
240
+ * for SSE backward-compat; NDJSON callers pass
241
+ * `application/x-ndjson`).
242
+ * - Does NOT arm an internal timeout — streams are long-lived; the
243
+ * 30s default would kill them after half a minute. Caller controls
244
+ * duration via `options.signal`. (`decisions.export` runs up to
245
+ * 5 minutes server-side.)
246
+ * - Returns the un-consumed `Response` so the resource can read
247
+ * `response.body` as a stream of bytes (for an SSE / NDJSON parser).
248
+ * - On non-2xx, drains the body and throws `AttestryAPIError` exactly
249
+ * like the JSON path so consumers see the same error shape.
250
+ * - On a 2xx with the WRONG content-type, throws with a clear message —
251
+ * defensive against a misconfigured proxy returning `text/html` (LB
252
+ * error page) or `text/plain`. The kernel always sets the right
253
+ * content-type for each route, so this is a fail-fast guard. The
254
+ * `expectedContentType` parameter (default `text/event-stream`)
255
+ * drives both the `Accept:` request header AND this guard, so SSE
256
+ * callers reject NDJSON responses and vice versa — a single source
257
+ * of truth.
258
+ *
259
+ * Retry semantics: the INITIAL fetch goes through the retry wrapper
260
+ * (same 429 logic as `request<T>`). Once the response is open,
261
+ * mid-iteration errors do NOT retry — events would be lost or
262
+ * duplicated without per-event idempotency. Caller manages reconnection
263
+ * by passing the last seen cursor back (e.g., SSE `lastEventId`,
264
+ * NDJSON: re-issue with adjusted filters).
265
+ */
266
+ export async function streamRequest(args) {
267
+ // Retry on the INITIAL fetch only. Once the response is open and
268
+ // we've started reading frames, any error (mid-stream 429 from a
269
+ // proxy, network drop, server timeout) bubbles to the consumer for
270
+ // them to decide whether to reconnect. Auto-retrying mid-stream
271
+ // would risk dropping or duplicating events.
272
+ const retry = resolveRetryOptions(args.config.retry, args.options?.retry);
273
+ const callerSignal = args.options?.signal;
274
+ let attempt = 0;
275
+ while (true) {
276
+ try {
277
+ return await streamRequestOnce(args);
278
+ }
279
+ catch (err) {
280
+ if (isRetryableError(err) &&
281
+ attempt < retry.maxRetries) {
282
+ const delay = computeRetryDelay(err, attempt, retry);
283
+ await sleepWithSignal(delay, callerSignal);
284
+ attempt++;
285
+ continue;
286
+ }
287
+ throw err;
288
+ }
289
+ }
290
+ }
291
+ /**
292
+ * Internal single-attempt streaming request. Same shape as `streamRequest`
293
+ * but runs exactly one fetch; the outer wrapper handles retry.
294
+ */
295
+ async function streamRequestOnce(args) {
296
+ const { config, path, query, headers: extraHeaders, options } = args;
297
+ // Default for SSE backward-compat. NDJSON callers pass
298
+ // `"application/x-ndjson"`; future content-types slot in here too.
299
+ const expectedContentType = args.expectedContentType ?? "text/event-stream";
300
+ const callerSignal = options?.signal;
301
+ if (callerSignal?.aborted) {
302
+ throw new AttestryError("request aborted by caller", {
303
+ cause: callerSignal.reason,
304
+ });
305
+ }
306
+ const queryString = encodeQuery(query);
307
+ const url = composeUrl(config.baseUrl, path) + queryString;
308
+ const headers = new Headers();
309
+ headers.set("x-api-key", config.apiKey);
310
+ headers.set("Accept", expectedContentType);
311
+ if (extraHeaders) {
312
+ for (const [k, v] of Object.entries(extraHeaders))
313
+ headers.set(k, v);
314
+ }
315
+ let response;
316
+ try {
317
+ response = await config.fetchImpl(url, {
318
+ method: "GET",
319
+ headers,
320
+ // Pass the caller's signal through directly — no internal abort
321
+ // controller, no timeout. A stream lives until the server closes
322
+ // it OR the caller aborts.
323
+ signal: callerSignal,
324
+ });
325
+ }
326
+ catch (err) {
327
+ throw new AttestryError(describeFetchFailure(err, false,
328
+ // `?? false` defends against an undefined caller signal — both
329
+ // branches reachable but the false-fallback only fires when no
330
+ // options.signal was passed AND the fetch rejected. Marginal
331
+ // pin value; defense-in-depth marker for v8.
332
+ /* v8 ignore next */
333
+ callerSignal?.aborted ?? false), { cause: err });
334
+ }
335
+ if (!response.ok) {
336
+ // Same body-extraction contract as the JSON path: parsed JSON if
337
+ // possible, raw text fallback. Non-JSON error bodies (LB 502 HTML,
338
+ // proxy text) surface as `details` rather than `null`.
339
+ const { parsed, raw } = await readBody(response);
340
+ // Defensive ?? fallbacks: `parsed` is null when the body isn't
341
+ // JSON; `raw.length > 0 ? raw : null` selects between the raw text
342
+ // and explicit null. `extractMessage(detail) ??` falls back to a
343
+ // generic "Attestry API returned N" when the body has no
344
+ // message/error field. All branches reachable in principle but a
345
+ // 4xx with neither JSON body nor extractable message is exotic —
346
+ // covered defensively.
347
+ /* v8 ignore next */
348
+ const detail = parsed ?? (raw.length > 0 ? raw : null);
349
+ const apiErr = new AttestryAPIError(
350
+ /* v8 ignore next */
351
+ extractMessage(detail) ?? `Attestry API returned ${response.status}`, response.status, detail);
352
+ attachRetryAfter(apiErr, response.headers.get("retry-after"));
353
+ throw apiErr;
354
+ }
355
+ // Defensive content-type check. The kernel always emits the
356
+ // expected content-type for each route. If a proxy / load balancer
357
+ // rewrites the response (e.g. an HTML error page wrapped at 200), the
358
+ // downstream parser would produce nonsense or hang. Fail-fast with a
359
+ // clear error class so consumers can branch on it.
360
+ //
361
+ // Hostile-review fix: parse the MIME type out of the header (per
362
+ // RFC 7231 §3.1.1.1, `media-type = type "/" subtype *( OWS ";" OWS
363
+ // parameter )`) and compare for exact equality with `expectedContentType`.
364
+ // The previous substring `includes()` match was bypassed by:
365
+ // - superset attacks (`application/x-ndjson-evil` matches
366
+ // `application/x-ndjson`),
367
+ // - parameter-injection attacks (`text/html; x-real-content=
368
+ // application/x-ndjson` matches), and
369
+ // - structured-suffix attacks (`application/foo+x-ndjson`).
370
+ // Pulling the type/subtype before the first `;`, trimming OWS, and
371
+ // exact-matching closes all three while still accepting legitimate
372
+ // parameters such as `; charset=utf-8`.
373
+ // `?? ""` defends against a null Content-Type header — the kernel
374
+ // always sets it, so the null branch is exotic. Defense-in-depth.
375
+ /* v8 ignore next */
376
+ const contentType = response.headers.get("content-type") ?? "";
377
+ const semicolonIdx = contentType.indexOf(";");
378
+ const contentTypeMime = (semicolonIdx === -1 ? contentType : contentType.slice(0, semicolonIdx))
379
+ .trim()
380
+ .toLowerCase();
381
+ const expectedMime = expectedContentType.trim().toLowerCase();
382
+ if (contentTypeMime !== expectedMime) {
383
+ throw new AttestryAPIError(`expected ${expectedContentType} response, got "${contentType}"`, response.status, null);
384
+ }
385
+ return response;
386
+ }
387
+ function encodeQuery(q) {
388
+ if (!q)
389
+ return "";
390
+ const params = [];
391
+ for (const [key, value] of Object.entries(q)) {
392
+ if (value === undefined || value === null)
393
+ continue;
394
+ params.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
395
+ }
396
+ return params.length > 0 ? `?${params.join("&")}` : "";
397
+ }
398
+ /**
399
+ * Reads the response body and tries to parse as JSON. Returns BOTH the
400
+ * parsed value (or null on parse failure / empty body) AND the raw text.
401
+ * Callers on the success path use only `parsed`; callers on the error
402
+ * path fall back to `raw` when `parsed` is null so non-JSON error bodies
403
+ * (LB 502 HTML, plain-text proxy errors) are surfaced as
404
+ * `AttestryAPIError.details` rather than silently lost. (Hostile-review
405
+ * H1.)
406
+ */
407
+ async function readBody(response) {
408
+ if (response.status === 204)
409
+ return { parsed: null, raw: "" };
410
+ const text = await response.text();
411
+ if (text.length === 0)
412
+ return { parsed: null, raw: "" };
413
+ try {
414
+ return { parsed: JSON.parse(text), raw: text };
415
+ }
416
+ catch {
417
+ return { parsed: null, raw: text };
418
+ }
419
+ }
420
+ function extractMessage(detail) {
421
+ if (detail && typeof detail === "object" && "error" in detail) {
422
+ const err = detail.error;
423
+ if (typeof err === "string")
424
+ return err;
425
+ if (err && typeof err === "object" && "message" in err) {
426
+ const m = err.message;
427
+ if (typeof m === "string")
428
+ return m;
429
+ }
430
+ }
431
+ if (detail && typeof detail === "object" && "message" in detail) {
432
+ const m = detail.message;
433
+ if (typeof m === "string")
434
+ return m;
435
+ }
436
+ return null;
437
+ }
438
+ function describeFetchFailure(err, abortFired, callerAborted) {
439
+ if (callerAborted)
440
+ return "request aborted by caller";
441
+ if (abortFired)
442
+ return "request timed out";
443
+ if (err instanceof Error)
444
+ return `network error: ${err.message}`;
445
+ return "network error";
446
+ }
447
+ // Test-only handles for direct pinning of internals.
448
+ export const __test__ = {
449
+ DEFAULT_BASE_URL,
450
+ DEFAULT_TIMEOUT_MS,
451
+ encodeQuery,
452
+ readBody,
453
+ extractMessage,
454
+ };
455
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import type { RetryOptions } from "./retry.js";
2
+ /**
3
+ * Minimal subset of the standard `fetch` signature. The SDK accepts any
4
+ * implementation that matches this shape so consumers can inject a custom
5
+ * `fetch` (testing, retry middleware, observability instrumentation, etc.)
6
+ * without depending on the global `fetch`.
7
+ */
8
+ export type FetchLike = (input: string | URL, init?: RequestInit) => Promise<Response>;
9
+ export interface AttestryClientOptions {
10
+ /**
11
+ * API key from the Attestry org settings page. Sent as the `x-api-key`
12
+ * header on every request. Required.
13
+ */
14
+ apiKey: string;
15
+ /**
16
+ * Base URL of the Attestry API. Defaults to `https://app.attestry.ai`.
17
+ * Override for self-hosted, EU residency, or local dev.
18
+ */
19
+ baseUrl?: string;
20
+ /**
21
+ * Custom fetch implementation. Defaults to the global `fetch`. Throws at
22
+ * construction time if neither is available.
23
+ */
24
+ fetch?: FetchLike;
25
+ /**
26
+ * Request timeout in milliseconds. Defaults to 30_000 (30s). Set `0` to
27
+ * disable. The SDK aborts via `AbortController` when the timeout elapses
28
+ * and surfaces the abort as `AttestryError` ("request timed out").
29
+ *
30
+ * Note: NOT applied to streaming requests (`*.stream()` methods).
31
+ * Streams are long-lived; the caller controls duration via `options.signal`.
32
+ */
33
+ timeoutMs?: number;
34
+ /**
35
+ * Automatic retry configuration for HTTP 429 responses. Per-call
36
+ * `RequestOptions.retry` overrides this. Set `{maxRetries: 0}` to
37
+ * disable retries client-wide.
38
+ *
39
+ * Defaults: `{maxRetries: 3, initialDelayMs: 1000, maxDelayMs: 30_000,
40
+ * honorRetryAfter: true}`.
41
+ *
42
+ * Applied to all JSON requests AND to the initial fetch of streaming
43
+ * requests. Mid-stream errors are NEVER retried (would risk lost or
44
+ * duplicated events).
45
+ */
46
+ retry?: Partial<RetryOptions>;
47
+ }
48
+ /**
49
+ * Per-request overrides.
50
+ */
51
+ export interface RequestOptions {
52
+ /** Caller-provided abort signal — composed with the SDK's internal timeout. */
53
+ signal?: AbortSignal;
54
+ /**
55
+ * Per-call retry override. Same shape as `AttestryClientOptions.retry`
56
+ * but takes precedence. Useful for one-off "do not retry this" calls
57
+ * or for tightening retries on a latency-sensitive call.
58
+ */
59
+ retry?: Partial<RetryOptions>;
60
+ }
61
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,CACtB,KAAK,EAAE,MAAM,GAAG,GAAG,EACnB,IAAI,CAAC,EAAE,WAAW,KACf,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEvB,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;CAC/B"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ // ─── Shared SDK types ───────────────────────────────────────────────────────
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,+EAA+E"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@attestry/sdk",
3
+ "version": "0.6.0",
4
+ "description": "Official TypeScript SDK for the Attestry compliance kernel \u2014 submit AI incidents, fetch decisions, query the cross-tenant pattern corpus.",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/attestry-ai/attestry-sdk.git",
9
+ "directory": "packages/attestry-sdk"
10
+ },
11
+ "type": "module",
12
+ "main": "dist/index.js",
13
+ "types": "dist/index.d.ts",
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "keywords": [
20
+ "attestry",
21
+ "ai-compliance",
22
+ "eu-ai-act",
23
+ "colorado-ai-act",
24
+ "nist-ai-rmf",
25
+ "iso-42001",
26
+ "ai-incidents",
27
+ "compliance-sdk"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsc",
31
+ "dev": "tsc --watch",
32
+ "test": "vitest run",
33
+ "test:watch": "vitest",
34
+ "prepare": "npm run build"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^22.0.0",
38
+ "typescript": "^5.7.0",
39
+ "vitest": "^3.0.0"
40
+ },
41
+ "engines": {
42
+ "node": ">=18.0.0"
43
+ }
44
+ }