@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.
- package/LICENSE +190 -0
- package/README.md +1269 -0
- package/dist/client.d.ts +58 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +74 -0
- package/dist/client.js.map +1 -0
- package/dist/constants.d.ts +7 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +43 -0
- package/dist/constants.js.map +1 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +41 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/lines-parser.d.ts +50 -0
- package/dist/lines-parser.d.ts.map +1 -0
- package/dist/lines-parser.js +211 -0
- package/dist/lines-parser.js.map +1 -0
- package/dist/ndjson-parser.d.ts +57 -0
- package/dist/ndjson-parser.d.ts.map +1 -0
- package/dist/ndjson-parser.js +245 -0
- package/dist/ndjson-parser.js.map +1 -0
- package/dist/resources/abac-policies.d.ts +1034 -0
- package/dist/resources/abac-policies.d.ts.map +1 -0
- package/dist/resources/abac-policies.js +1519 -0
- package/dist/resources/abac-policies.js.map +1 -0
- package/dist/resources/audit-log.d.ts +588 -0
- package/dist/resources/audit-log.d.ts.map +1 -0
- package/dist/resources/audit-log.js +629 -0
- package/dist/resources/audit-log.js.map +1 -0
- package/dist/resources/batch.d.ts +845 -0
- package/dist/resources/batch.d.ts.map +1 -0
- package/dist/resources/batch.js +1074 -0
- package/dist/resources/batch.js.map +1 -0
- package/dist/resources/chat.d.ts +151 -0
- package/dist/resources/chat.d.ts.map +1 -0
- package/dist/resources/chat.js +124 -0
- package/dist/resources/chat.js.map +1 -0
- package/dist/resources/check.d.ts +348 -0
- package/dist/resources/check.d.ts.map +1 -0
- package/dist/resources/check.js +543 -0
- package/dist/resources/check.js.map +1 -0
- package/dist/resources/compliance-check.d.ts +330 -0
- package/dist/resources/compliance-check.d.ts.map +1 -0
- package/dist/resources/compliance-check.js +402 -0
- package/dist/resources/compliance-check.js.map +1 -0
- package/dist/resources/decisions.d.ts +1208 -0
- package/dist/resources/decisions.d.ts.map +1 -0
- package/dist/resources/decisions.js +1362 -0
- package/dist/resources/decisions.js.map +1 -0
- package/dist/resources/evidence-pack.d.ts +1080 -0
- package/dist/resources/evidence-pack.d.ts.map +1 -0
- package/dist/resources/evidence-pack.js +1789 -0
- package/dist/resources/evidence-pack.js.map +1 -0
- package/dist/resources/gate.d.ts +613 -0
- package/dist/resources/gate.d.ts.map +1 -0
- package/dist/resources/gate.js +737 -0
- package/dist/resources/gate.js.map +1 -0
- package/dist/resources/incidents.d.ts +136 -0
- package/dist/resources/incidents.d.ts.map +1 -0
- package/dist/resources/incidents.js +229 -0
- package/dist/resources/incidents.js.map +1 -0
- package/dist/resources/regulatory-changes.d.ts +307 -0
- package/dist/resources/regulatory-changes.d.ts.map +1 -0
- package/dist/resources/regulatory-changes.js +365 -0
- package/dist/resources/regulatory-changes.js.map +1 -0
- package/dist/resources/safe-input-read.d.ts +21 -0
- package/dist/resources/safe-input-read.d.ts.map +1 -0
- package/dist/resources/safe-input-read.js +57 -0
- package/dist/resources/safe-input-read.js.map +1 -0
- package/dist/resources/ship-gate.d.ts +475 -0
- package/dist/resources/ship-gate.d.ts.map +1 -0
- package/dist/resources/ship-gate.js +727 -0
- package/dist/resources/ship-gate.js.map +1 -0
- package/dist/resources/vision.d.ts +540 -0
- package/dist/resources/vision.d.ts.map +1 -0
- package/dist/resources/vision.js +1036 -0
- package/dist/resources/vision.js.map +1 -0
- package/dist/retry.d.ts +103 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +224 -0
- package/dist/retry.js.map +1 -0
- package/dist/sse-parser.d.ts +64 -0
- package/dist/sse-parser.d.ts.map +1 -0
- package/dist/sse-parser.js +271 -0
- package/dist/sse-parser.js.map +1 -0
- package/dist/transport.d.ts +142 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +455 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,845 @@
|
|
|
1
|
+
import type { AttestryClient } from "../client.js";
|
|
2
|
+
import type { RequestOptions } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Closed enum for `BatchSubmitInput.jobType`. Mirrors the kernel's
|
|
5
|
+
* `batchSubmitSchema.jobType` at `src/app/api/v1/batch/route.ts:30`:
|
|
6
|
+
* `z.enum(["classify", "assess", "classify_and_assess"])`.
|
|
7
|
+
*
|
|
8
|
+
* Frozen at module load to prevent runtime mutation by hostile /
|
|
9
|
+
* buggy dependencies (P1 hardening — mirrors the freeze on
|
|
10
|
+
* `AUDIT_LOG_EXPORT_FORMATS` / `CHAT_MESSAGE_ROLES` etc.). Iterating
|
|
11
|
+
* `for (const t of BATCH_JOB_TYPES)` lists the 3 valid values; the
|
|
12
|
+
* SDK's `submit()` pre-rejects any unknown string with `TypeError`
|
|
13
|
+
* (invariant #41 — closed-enum SDK pre-rejection-eligible surface).
|
|
14
|
+
*
|
|
15
|
+
* Drift-pinned in `sdk-drift.test.ts` spec-diff round against the
|
|
16
|
+
* kernel's Zod enum to catch a kernel-side widening (e.g., adding
|
|
17
|
+
* `"generate_docs"` — the stale db/schema.ts:1030 comment mentions
|
|
18
|
+
* this value but the route's Zod schema does NOT include it; the
|
|
19
|
+
* comment is stale, the Zod is authoritative).
|
|
20
|
+
*
|
|
21
|
+
* **Semantics of each value**:
|
|
22
|
+
* - `"classify"` — run the rule-based classifier on each system
|
|
23
|
+
* and PERSIST the new `riskClassifications` to the system row
|
|
24
|
+
* (write-side effect). Per-row `classifications` contains the
|
|
25
|
+
* fresh classification.
|
|
26
|
+
* - `"assess"` — emit each system's CURRENT `riskClassifications`
|
|
27
|
+
* state from the DB (read-only; no write side effect, despite
|
|
28
|
+
* `WRITE_ASSESSMENTS` being a valid auth permission). Per-row
|
|
29
|
+
* `classifications` contains whatever was already on the row
|
|
30
|
+
* (may be `null` if no prior classification).
|
|
31
|
+
* - `"classify_and_assess"` — same as `"classify"` (the kernel
|
|
32
|
+
* branches `classify || classify_and_assess` together at
|
|
33
|
+
* route.ts:112). The two-name distinction is purely semantic
|
|
34
|
+
* for the consumer — both write the new classification and
|
|
35
|
+
* emit it.
|
|
36
|
+
*/
|
|
37
|
+
export declare const BATCH_JOB_TYPES: readonly ["classify", "assess", "classify_and_assess"];
|
|
38
|
+
export type BatchJobType = (typeof BATCH_JOB_TYPES)[number];
|
|
39
|
+
/**
|
|
40
|
+
* Closed enum for `BatchJobStatus.status` (the GET response's wider
|
|
41
|
+
* batch-job-level status). Mirrors the kernel's
|
|
42
|
+
* `schema.batchJobs.status` DB column comment at
|
|
43
|
+
* `src/lib/db/schema.ts:1031`: `'pending' | 'processing' | 'completed' |
|
|
44
|
+
* 'failed'`.
|
|
45
|
+
*
|
|
46
|
+
* **Wider than the POST response's `status`** — `submit()` returns
|
|
47
|
+
* `status: "completed" | "failed"` only (computed at handler end from
|
|
48
|
+
* `failed === total`). A `get()` call against a job submitted by the
|
|
49
|
+
* SDK observes only `"completed" | "failed"` in practice (the job
|
|
50
|
+
* processed inline before the row was committed), but a `get()`
|
|
51
|
+
* against a job submitted via a future async path OR an out-of-band
|
|
52
|
+
* caller mid-flight could observe `"pending"` / `"processing"`.
|
|
53
|
+
*
|
|
54
|
+
* Frozen at module load (P1 hardening — same rationale as
|
|
55
|
+
* `BATCH_JOB_TYPES`). Iterating `for (const s of BATCH_JOB_STATUSES)`
|
|
56
|
+
* lists all 4 values. **The DB column has no kernel-side enum
|
|
57
|
+
* constraint** — the SDK exposes the closed union at the type level
|
|
58
|
+
* but the runtime validator checks `typeof status === "string"` only
|
|
59
|
+
* (faithful courier — same asymmetry as gate's `gate: "pass" |
|
|
60
|
+
* "fail"` pattern).
|
|
61
|
+
*
|
|
62
|
+
* Drift-pinned in the spec-diff round (sdk-drift.test.ts —
|
|
63
|
+
* "BATCH_JOB_STATUSES in SDK matches the kernel db/schema.ts
|
|
64
|
+
* comment + .default() literal") via TWO assertions inside the
|
|
65
|
+
* same `it()`:
|
|
66
|
+
* - **Assertion 1**: the schema's `.default("pending")` literal
|
|
67
|
+
* at `src/lib/db/schema.ts:1031` (machine-checkable). Fires
|
|
68
|
+
* on a default-change (e.g., from `"pending"` to `"queued"`).
|
|
69
|
+
* - **Assertion 2**: the schema column COMMENT listing all 4
|
|
70
|
+
* values (documentation source-of-truth). Fires on a
|
|
71
|
+
* widening (e.g., a new `"cancelled"` status added to the
|
|
72
|
+
* comment) OR a comment removal.
|
|
73
|
+
* The pin's failure message distinguishes which assertion fires.
|
|
74
|
+
*/
|
|
75
|
+
export declare const BATCH_JOB_STATUSES: readonly ["pending", "processing", "completed", "failed"];
|
|
76
|
+
export type BatchJobStatusValue = (typeof BATCH_JOB_STATUSES)[number];
|
|
77
|
+
/**
|
|
78
|
+
* One per-system result row in `BatchSubmitResponse.results` /
|
|
79
|
+
* `BatchJobStatus.results`. Source-of-truth at kernel
|
|
80
|
+
* `src/app/api/v1/batch/route.ts:42-48` (`BatchSystemResult`
|
|
81
|
+
* interface).
|
|
82
|
+
*
|
|
83
|
+
* **Discriminator pattern**: branch on `status: "success" | "error"`
|
|
84
|
+
* (closed-enum string match). The kernel guarantees `classifications`
|
|
85
|
+
* is present on `"success"` rows and `errorMessage` is present on
|
|
86
|
+
* `"error"` rows — but **do NOT use `row.errorMessage === undefined`
|
|
87
|
+
* or `row.classifications === undefined` as the discriminator**.
|
|
88
|
+
* Under `Object.prototype.errorMessage = <value>` pollution, the
|
|
89
|
+
* `=== undefined` equality walks the prototype and reads the polluted
|
|
90
|
+
* value — returning false even when the own-property is genuinely
|
|
91
|
+
* absent, silently misclassifying an `"error"` row as `"success"`.
|
|
92
|
+
* The `status` field is the pollution-safe discriminator (the SDK's
|
|
93
|
+
* own-property check would reject a missing `status` field anyway).
|
|
94
|
+
*
|
|
95
|
+
* **`classifications` is `unknown`** because the kernel emits the
|
|
96
|
+
* full `classifySystem()` output OR the system's
|
|
97
|
+
* `riskClassifications` jsonb cell — both open-spec objects whose
|
|
98
|
+
* exact shape lives in `src/lib/classification.ts`. Consumers
|
|
99
|
+
* casting to a specific shape should align with that module.
|
|
100
|
+
*
|
|
101
|
+
* **`errorMessage` is `string`** — the kernel scrubs at
|
|
102
|
+
* route.ts:153-157 in this order: (1) take first line via
|
|
103
|
+
* `.split("\n")[0]`, (2) **TRUNCATE** to 500 chars via
|
|
104
|
+
* `.slice(0, 500)`, THEN (3) **REDACT** matches of:
|
|
105
|
+
* - alternation `(?:password|secret|token|key)` (literal,
|
|
106
|
+
* case-insensitive),
|
|
107
|
+
* - followed by `=`,
|
|
108
|
+
* - followed by **zero-or-more** characters that are NOT
|
|
109
|
+
* whitespace and NOT `&` (so `password=` with empty value
|
|
110
|
+
* IS matched; the actual kernel regex flags are `g` + `i`).
|
|
111
|
+
*
|
|
112
|
+
* **Order matters**: truncation happens BEFORE redaction, so a
|
|
113
|
+
* credential straddling the 500-char boundary may be partially
|
|
114
|
+
* sliced (the regex's zero-or-more arm matches the truncated
|
|
115
|
+
* prefix, redacting whatever survived the slice — but the
|
|
116
|
+
* sliced-off suffix is gone before redaction; whatever made it
|
|
117
|
+
* past the slice gets redacted to `[REDACTED]`). The regex only
|
|
118
|
+
* catches `<keyword>=<value>` shapes — Bearer tokens,
|
|
119
|
+
* JSON-quoted secrets like `"token":"abc"`, or other formats
|
|
120
|
+
* are NOT scrubbed. The SDK does NOT add a second scrubbing
|
|
121
|
+
* pass — faithful courier.
|
|
122
|
+
*
|
|
123
|
+
* Note: per-row shape is NOT P2-validated by the SDK (`Array.isArray`
|
|
124
|
+
* only on the `results` array — same faithful-courier pattern as
|
|
125
|
+
* `gate.evaluate`'s `gaps: GateGap[]`). The kernel emits well-formed
|
|
126
|
+
* rows reliably; consumers who paranoid-validate should do so post-
|
|
127
|
+
* hoc.
|
|
128
|
+
*/
|
|
129
|
+
export interface BatchSystemResult {
|
|
130
|
+
/**
|
|
131
|
+
* UUID of the system this row describes. Always echoes the
|
|
132
|
+
* corresponding entry in `BatchSubmitInput.systemIds`.
|
|
133
|
+
*/
|
|
134
|
+
systemId: string;
|
|
135
|
+
/**
|
|
136
|
+
* Human-readable system name from `schema.aiSystems.name`. The
|
|
137
|
+
* kernel uses `systemMap.get(systemId)?.name ?? "Unknown"` at
|
|
138
|
+
* route.ts:161 — a defensive fallback that is **dead code today**.
|
|
139
|
+
* Verified: (a) the prior membership check at route.ts:79-85
|
|
140
|
+
* guarantees every `systemId` is present in `systemMap`, so
|
|
141
|
+
* `systemMap.get(...)?.name` cannot return undefined via the `?.`
|
|
142
|
+
* arm; (b) `schema.aiSystems.name` is `text("name").notNull()` at
|
|
143
|
+
* `src/lib/db/schema.ts:156` — the column cannot be null, so the
|
|
144
|
+
* `?? "Unknown"` arm cannot fire either. Both arms are unreachable
|
|
145
|
+
* under the current schema; the fallback exists for defense-in-
|
|
146
|
+
* depth if a future migration relaxes the `notNull()` constraint.
|
|
147
|
+
* The SDK passes the literal string `"Unknown"` through faithfully
|
|
148
|
+
* in that hypothetical future.
|
|
149
|
+
*/
|
|
150
|
+
systemName: string;
|
|
151
|
+
/**
|
|
152
|
+
* Closed enum — discriminator for `classifications` vs
|
|
153
|
+
* `errorMessage`. Use `row.status === "success"` (closed-enum
|
|
154
|
+
* string match) to branch — NOT `row.errorMessage === undefined`
|
|
155
|
+
* (prototype-pollution-unsafe).
|
|
156
|
+
*/
|
|
157
|
+
status: "success" | "error";
|
|
158
|
+
/**
|
|
159
|
+
* Present only when `status === "success"`. The system's
|
|
160
|
+
* classification output (the kernel's `classifySystem()` result
|
|
161
|
+
* for `"classify"` / `"classify_and_assess"` job types, OR the
|
|
162
|
+
* system's CURRENT `riskClassifications` for the `"assess"` job
|
|
163
|
+
* type). Open-spec — typed `unknown` because the underlying
|
|
164
|
+
* `classifySystem` return shape lives outside the kernel route
|
|
165
|
+
* (`src/lib/classification.ts`).
|
|
166
|
+
*/
|
|
167
|
+
classifications?: unknown;
|
|
168
|
+
/**
|
|
169
|
+
* Present only when `status === "error"`. Human-readable error
|
|
170
|
+
* message. **Kernel-scrubbed** at `route.ts:153-157` in this
|
|
171
|
+
* order: (1) first line only via `.split("\n")[0]`, (2)
|
|
172
|
+
* **TRUNCATE** to 500 chars via `.slice(0, 500)`, then (3)
|
|
173
|
+
* **REDACT** matches of `(?:password|secret|token|key)`
|
|
174
|
+
* followed by `=` followed by **zero-or-more** characters
|
|
175
|
+
* that are NOT whitespace and NOT `&` (case-insensitive,
|
|
176
|
+
* global). **`password=` with empty value IS matched and
|
|
177
|
+
* redacted**; the regex's quantifier is zero-or-more, not
|
|
178
|
+
* one-or-more. **Order matters**: truncation BEFORE redaction
|
|
179
|
+
* means a credential straddling the 500-char boundary is
|
|
180
|
+
* sliced before the regex sees it — whatever survived the
|
|
181
|
+
* slice gets redacted; whatever was sliced off is gone. The
|
|
182
|
+
* regex only catches `<keyword>=<value>` shapes — Bearer
|
|
183
|
+
* tokens, JSON-quoted secrets (`"token":"abc"`), or other
|
|
184
|
+
* formats are NOT scrubbed. The SDK does NOT add a second
|
|
185
|
+
* scrubbing pass — faithful courier.
|
|
186
|
+
*/
|
|
187
|
+
errorMessage?: string;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Optional config object on `BatchSubmitInput.config`. Mirrors the
|
|
191
|
+
* kernel's `batchSubmitSchema.config` at
|
|
192
|
+
* `src/app/api/v1/batch/route.ts:35-39` — a single-field wrapper
|
|
193
|
+
* around `frameworks?`. The wrapper (rather than top-level fields)
|
|
194
|
+
* matches the kernel wire literal `{config: {frameworks}}` and
|
|
195
|
+
* leaves room for future top-level additions without breaking the
|
|
196
|
+
* surface.
|
|
197
|
+
*
|
|
198
|
+
* Round-tripped on `get()` (the kernel persists `config` to the
|
|
199
|
+
* `batch_jobs.config` jsonb column and emits it back on GET — see
|
|
200
|
+
* `BatchJobStatus.config`).
|
|
201
|
+
*
|
|
202
|
+
* **`config.frameworks` carry-forward from gate / check exactly** —
|
|
203
|
+
* array length ≤20 + per-element string length [1, 100]. The kernel
|
|
204
|
+
* does NOT pin `frameworks` to a closed enum of values; any string
|
|
205
|
+
* within the length bounds is accepted. Today the kernel does NOT
|
|
206
|
+
* use `config.frameworks` in the inline classification path
|
|
207
|
+
* (`classifySystem` doesn't take a frameworks filter) — the field is
|
|
208
|
+
* persisted for forward-compat with future job types but has NO
|
|
209
|
+
* visible effect on the current `classify` / `assess` paths.
|
|
210
|
+
* Documented as a kernel surface gap; consumers passing
|
|
211
|
+
* `config.frameworks` today get round-trip-only behavior.
|
|
212
|
+
*/
|
|
213
|
+
export interface BatchConfig {
|
|
214
|
+
/**
|
|
215
|
+
* Optional array of framework identifiers. Up to 20 entries; each
|
|
216
|
+
* string of length 1-100. Round-tripped to `BatchJobStatus.config
|
|
217
|
+
* .frameworks` on `get()`; no visible effect on the current
|
|
218
|
+
* inline classification paths.
|
|
219
|
+
*/
|
|
220
|
+
frameworks?: string[];
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Input shape for `batch.submit()`. Source-of-truth at kernel
|
|
224
|
+
* `src/app/api/v1/batch/route.ts:29-40` (`batchSubmitSchema`).
|
|
225
|
+
*
|
|
226
|
+
* **`jobType`** — REQUIRED, closed enum of 3 strings (`BatchJobType`).
|
|
227
|
+
* The SDK pre-validates membership in `BATCH_JOB_TYPES`
|
|
228
|
+
* synchronously (invariant #41 — closed-enum SDK pre-rejection-
|
|
229
|
+
* eligible). `TypeError` for an unknown value lists the valid set.
|
|
230
|
+
*
|
|
231
|
+
* **`systemIds`** — REQUIRED array of 1-50 UUIDs. The SDK pre-
|
|
232
|
+
* validates: `Array.isArray`, length [1, 50] inclusive, each
|
|
233
|
+
* element a non-empty string matching the RFC 4122 UUID regex (D4
|
|
234
|
+
* — SDK pre-validates closed-spec rule per invariant #49). Snapshot
|
|
235
|
+
* via `Array.from` up front for TOCTOU defense (a Proxy whose
|
|
236
|
+
* `.length` or `[i]` changes between reads can't slip past
|
|
237
|
+
* validation). **The `.min(1)` is new** — gate/check's `frameworks`
|
|
238
|
+
* allowed empty arrays via `.optional().max(20)`; batch's
|
|
239
|
+
* `systemIds` rejects empty at the Zod level, so the SDK also
|
|
240
|
+
* pre-rejects empty.
|
|
241
|
+
*
|
|
242
|
+
* **`config`** — OPTIONAL. When provided, must be a non-null
|
|
243
|
+
* non-array object. `config.frameworks` (when provided) is an
|
|
244
|
+
* array of 1-100-char strings, length ≤20 (carry-forward from
|
|
245
|
+
* gate / check). Today the kernel persists `config` to the row but
|
|
246
|
+
* does NOT use it in the inline classification path — the field is
|
|
247
|
+
* forward-compat. Documented as a kernel surface gap; consumers get
|
|
248
|
+
* round-trip-only behavior.
|
|
249
|
+
*
|
|
250
|
+
* **No defaults** — `batchSubmitSchema` has NO `.default()` clauses
|
|
251
|
+
* (carry-forward invariant #52 N/A here, asymmetric with gate's two
|
|
252
|
+
* defaults). All fields without explicit values are simply absent
|
|
253
|
+
* from the body when the consumer omits them.
|
|
254
|
+
*/
|
|
255
|
+
export interface BatchSubmitInput {
|
|
256
|
+
/**
|
|
257
|
+
* Closed enum: `"classify"` | `"assess"` | `"classify_and_assess"`.
|
|
258
|
+
* See `BATCH_JOB_TYPES` for semantics.
|
|
259
|
+
*/
|
|
260
|
+
jobType: BatchJobType;
|
|
261
|
+
/**
|
|
262
|
+
* 1-50 UUIDs (RFC 4122 hyphenated form, case-insensitive). The
|
|
263
|
+
* `.min(1)` is enforced kernel-side AND SDK-side (empty arrays
|
|
264
|
+
* rejected synchronously).
|
|
265
|
+
*/
|
|
266
|
+
systemIds: string[];
|
|
267
|
+
/**
|
|
268
|
+
* Optional. Round-tripped to `BatchJobStatus.config` on `get()`;
|
|
269
|
+
* no visible effect on the current inline classification paths.
|
|
270
|
+
*/
|
|
271
|
+
config?: BatchConfig;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Response shape returned by `batch.submit()`. 10 fields, ALL always
|
|
275
|
+
* present (no emit-only optionality — distinct from `gate.evaluate`).
|
|
276
|
+
*
|
|
277
|
+
* Source-of-truth at kernel `src/app/api/v1/batch/route.ts:197-208`
|
|
278
|
+
* (the `successResponse({...})` literal).
|
|
279
|
+
*
|
|
280
|
+
* **`status: "completed" | "failed"`** — kernel-computed at handler
|
|
281
|
+
* end (route.ts:170): `failed === body.systemIds.length ? "failed" :
|
|
282
|
+
* "completed"`. **STRICTLY NARROWER than `BatchJobStatus.status`**
|
|
283
|
+
* (the GET response uses the wider 4-value enum). A POST-submitted
|
|
284
|
+
* job is always fully-processed by the time the response is built,
|
|
285
|
+
* so `"pending"` / `"processing"` are never observed on this method.
|
|
286
|
+
* **Two distinct status enums on the same wire-shape family** —
|
|
287
|
+
* invariant candidate #56. Type contract is closed at the call site;
|
|
288
|
+
* runtime is open (faithful courier — P2 validator checks `typeof
|
|
289
|
+
* === "string"` only).
|
|
290
|
+
*
|
|
291
|
+
* **`results: BatchSystemResult[]`** — partial-success envelope.
|
|
292
|
+
* Each row describes one system's outcome with
|
|
293
|
+
* `status: "success" | "error"` plus either `classifications` or
|
|
294
|
+
* `errorMessage`. The call resolves successfully (no throw) even
|
|
295
|
+
* when every row failed — top-level failures (auth, rate limit, plan
|
|
296
|
+
* limit, Zod rejection, cross-org systemId, internal) DO throw
|
|
297
|
+
* `AttestryAPIError`. Mirror of `decisions.bulk`'s contract — the
|
|
298
|
+
* canonical SDK partial-success pattern.
|
|
299
|
+
*
|
|
300
|
+
* **`startedAt: string | null`** — the kernel sets `startedAt: new
|
|
301
|
+
* Date()` at insert time (route.ts:99) so in practice this is
|
|
302
|
+
* always a non-null ISO-8601 string. The `?? null` fallback at
|
|
303
|
+
* route.ts:206 is defensive against a schema migration making the
|
|
304
|
+
* column nullable; the SDK's wire-shape type follows the defensive
|
|
305
|
+
* shape (`string | null`).
|
|
306
|
+
*
|
|
307
|
+
* **`completedAt: string`** — ALWAYS a string in the POST response
|
|
308
|
+
* (route.ts:207 emits `new Date().toISOString()` unconditionally).
|
|
309
|
+
* Asymmetric with `BatchJobStatus.completedAt` which is nullable
|
|
310
|
+
* (DB column allows null for non-completed jobs).
|
|
311
|
+
*/
|
|
312
|
+
export interface BatchSubmitResponse {
|
|
313
|
+
/** UUID of the newly-created batch job row. */
|
|
314
|
+
id: string;
|
|
315
|
+
/**
|
|
316
|
+
* Echoes the input — one of the 3 `BatchJobType` values. Open at
|
|
317
|
+
* runtime (P2 validates `typeof === "string"` only).
|
|
318
|
+
*/
|
|
319
|
+
jobType: BatchJobType;
|
|
320
|
+
/**
|
|
321
|
+
* **Batch-job-level** status — closed to `"completed" | "failed"`
|
|
322
|
+
* on POST (kernel-computed). Open at runtime (P2 validates
|
|
323
|
+
* `typeof === "string"` only). Distinct from
|
|
324
|
+
* `results[i].status` (per-row `"success" | "error"`).
|
|
325
|
+
*/
|
|
326
|
+
status: "completed" | "failed";
|
|
327
|
+
/** Echoes `systemIds.length` from the input (1-50). */
|
|
328
|
+
totalSystems: number;
|
|
329
|
+
/**
|
|
330
|
+
* Count of rows whose `status === "success"`. May be less than
|
|
331
|
+
* `totalSystems` when some systems failed; the call still
|
|
332
|
+
* resolves successfully.
|
|
333
|
+
*/
|
|
334
|
+
processedSystems: number;
|
|
335
|
+
/**
|
|
336
|
+
* Count of rows whose `status === "error"`. Equals
|
|
337
|
+
* `totalSystems - processedSystems`. Both counts are kernel-
|
|
338
|
+
* authoritative (NOT derived client-side).
|
|
339
|
+
*/
|
|
340
|
+
failedSystems: number;
|
|
341
|
+
/**
|
|
342
|
+
* One entry per input `systemIds[i]`, in input order. Always
|
|
343
|
+
* present, always an array (the kernel processes every input row
|
|
344
|
+
* inline before emitting the response).
|
|
345
|
+
*/
|
|
346
|
+
results: BatchSystemResult[];
|
|
347
|
+
/** ISO-8601, from the row's `createdAt` column. */
|
|
348
|
+
createdAt: string;
|
|
349
|
+
/**
|
|
350
|
+
* ISO-8601 OR null. In practice always a string on POST (the
|
|
351
|
+
* kernel sets `startedAt: new Date()` at insert time); the null
|
|
352
|
+
* fallback is defensive against future schema changes.
|
|
353
|
+
*/
|
|
354
|
+
startedAt: string | null;
|
|
355
|
+
/**
|
|
356
|
+
* ISO-8601 — ALWAYS present on POST (kernel emits
|
|
357
|
+
* `new Date().toISOString()` unconditionally at route.ts:207).
|
|
358
|
+
* Asymmetric with `BatchJobStatus.completedAt` which is nullable.
|
|
359
|
+
*/
|
|
360
|
+
completedAt: string;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Response shape returned by `batch.get(id)`. 11 fields — includes
|
|
364
|
+
* `config` (POST omits config from its response because it's already
|
|
365
|
+
* in the request body, but GET re-emits for callers who didn't
|
|
366
|
+
* submit and want the full picture).
|
|
367
|
+
*
|
|
368
|
+
* Source-of-truth at kernel `src/app/api/v1/batch/[id]/route.ts:57-69`.
|
|
369
|
+
*
|
|
370
|
+
* **`status: BatchJobStatusValue`** — the WIDER 4-value enum
|
|
371
|
+
* (`"pending" | "processing" | "completed" | "failed"`). DB column
|
|
372
|
+
* pass-through; the runtime value MAY observe any of the four for
|
|
373
|
+
* non-SDK-submitted jobs. Type contract is closed at the call site;
|
|
374
|
+
* runtime is open (faithful courier).
|
|
375
|
+
*
|
|
376
|
+
* **`results: BatchSystemResult[] | null`** — nullable on GET (DB
|
|
377
|
+
* jsonb column allows null for `"pending"` jobs that haven't been
|
|
378
|
+
* processed yet). SDK-submitted jobs always have non-null `results`
|
|
379
|
+
* by the time their row is committed.
|
|
380
|
+
*
|
|
381
|
+
* **`config: BatchConfig | null`** — round-trips whatever was
|
|
382
|
+
* submitted (`null` when consumer omitted `config`, or the
|
|
383
|
+
* submitted shape when provided). Per-field shape is open-spec
|
|
384
|
+
* (faithful courier — P2 validates `config === null` OR
|
|
385
|
+
* `typeof === "object"` only).
|
|
386
|
+
*
|
|
387
|
+
* **`startedAt: string | null`** + **`completedAt: string | null`**
|
|
388
|
+
* — both nullable on GET. `"pending"` jobs have both null;
|
|
389
|
+
* `"processing"` has startedAt set but completedAt null;
|
|
390
|
+
* `"completed"` / `"failed"` have both set.
|
|
391
|
+
*/
|
|
392
|
+
export interface BatchJobStatus {
|
|
393
|
+
/** UUID of the batch job (echoes the `id` arg passed to `get()`). */
|
|
394
|
+
id: string;
|
|
395
|
+
/**
|
|
396
|
+
* One of the 3 `BatchJobType` values. Open at runtime (P2
|
|
397
|
+
* validates `typeof === "string"` only). DB column has no closed-
|
|
398
|
+
* enum constraint, but rows can only reach the table via POST
|
|
399
|
+
* which Zod-validates.
|
|
400
|
+
*/
|
|
401
|
+
jobType: BatchJobType;
|
|
402
|
+
/**
|
|
403
|
+
* **Batch-job-level** status — closed to the 4-value
|
|
404
|
+
* `BatchJobStatusValue` enum on GET (DB column pass-through).
|
|
405
|
+
* Open at runtime (P2 validates `typeof === "string"` only).
|
|
406
|
+
* Distinct from `results[i].status` (per-row `"success" |
|
|
407
|
+
* "error"`).
|
|
408
|
+
*/
|
|
409
|
+
status: BatchJobStatusValue;
|
|
410
|
+
/** Echoes the original input `systemIds.length`. */
|
|
411
|
+
totalSystems: number;
|
|
412
|
+
/** Count of rows whose `status === "success"`. */
|
|
413
|
+
processedSystems: number;
|
|
414
|
+
/** Count of rows whose `status === "error"`. */
|
|
415
|
+
failedSystems: number;
|
|
416
|
+
/**
|
|
417
|
+
* Array of per-system results OR `null`. **Nullable on GET** —
|
|
418
|
+
* `"pending"` jobs have not yet been processed and their
|
|
419
|
+
* `results` column is null. SDK-submitted jobs always have
|
|
420
|
+
* non-null `results` by the time the row is committed.
|
|
421
|
+
*/
|
|
422
|
+
results: BatchSystemResult[] | null;
|
|
423
|
+
/**
|
|
424
|
+
* The config object the submission used, OR `null`. **Three
|
|
425
|
+
* distinct round-trip cases**:
|
|
426
|
+
* - `null` when the caller OMITTED `config` from the submit
|
|
427
|
+
* body (kernel writes `body.config ?? null` to the column —
|
|
428
|
+
* omission becomes literal `null`).
|
|
429
|
+
* - `{}` (empty object) when the caller passed an explicit
|
|
430
|
+
* empty `config: {}` — kernel writes the empty object
|
|
431
|
+
* verbatim.
|
|
432
|
+
* - `{frameworks: [...]}` (or any other valid shape) when the
|
|
433
|
+
* caller passed an explicit config.
|
|
434
|
+
* **You CANNOT distinguish "consumer omitted" from "kernel wrote
|
|
435
|
+
* null" via this field on GET** — both surface as `null`. To
|
|
436
|
+
* detect explicit-empty-object, check `config !== null` after a
|
|
437
|
+
* known submission. Round-trip only — the kernel does NOT use
|
|
438
|
+
* `config.frameworks` in the current inline classification path.
|
|
439
|
+
*/
|
|
440
|
+
config: BatchConfig | null;
|
|
441
|
+
/** ISO-8601, from the row's `createdAt` column. */
|
|
442
|
+
createdAt: string;
|
|
443
|
+
/**
|
|
444
|
+
* ISO-8601 OR null. `"pending"` jobs have null; jobs that have
|
|
445
|
+
* been picked up by an inline or async worker have it set.
|
|
446
|
+
*/
|
|
447
|
+
startedAt: string | null;
|
|
448
|
+
/**
|
|
449
|
+
* ISO-8601 OR null. `"pending"` and `"processing"` jobs have
|
|
450
|
+
* null; `"completed"` / `"failed"` have it set.
|
|
451
|
+
*/
|
|
452
|
+
completedAt: string | null;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* `batch` resource — sibling to `IncidentsResource`,
|
|
456
|
+
* `DecisionsResource`, `ChatResource`, `AuditLogResource`,
|
|
457
|
+
* `RegulatoryChangesResource`, `ComplianceCheckResource`,
|
|
458
|
+
* `CheckResource`, `GateResource`.
|
|
459
|
+
*
|
|
460
|
+
* Multi-method resource (mirror of `ChatResource`'s `send` +
|
|
461
|
+
* `stream`). Wraps TWO kernel routes:
|
|
462
|
+
* - `POST /api/v1/batch` via `submit(input, options?)`
|
|
463
|
+
* - `GET /api/v1/batch/<UUID>` via `get(id, options?)`
|
|
464
|
+
*
|
|
465
|
+
* **First SDK resource with asymmetric auth between methods on the
|
|
466
|
+
* same resource** (invariant candidate #54). `submit()` requires a
|
|
467
|
+
* key with `CLASSIFY` or `WRITE_ASSESSMENTS` AND enterprise plan;
|
|
468
|
+
* `get()` requires only `READ_ASSESSMENTS`. See per-method JSDoc.
|
|
469
|
+
*/
|
|
470
|
+
export declare class BatchResource {
|
|
471
|
+
private readonly client;
|
|
472
|
+
constructor(client: AttestryClient);
|
|
473
|
+
/**
|
|
474
|
+
* Submit a batch job — classify and/or read the current
|
|
475
|
+
* classification state for 1-50 systems in one call. Returns a
|
|
476
|
+
* `BatchSubmitResponse` with a per-row `results` envelope
|
|
477
|
+
* describing each system's outcome.
|
|
478
|
+
*
|
|
479
|
+
* **Partial-success contract**: the call resolves successfully
|
|
480
|
+
* (no throw) even when every row failed. Inspect
|
|
481
|
+
* `response.failedSystems` (or iterate `response.results` filtering
|
|
482
|
+
* `row.status === "error"`) to detect per-row errors. Top-level
|
|
483
|
+
* failures (auth, plan, rate limit, Zod, cross-org systemId,
|
|
484
|
+
* internal) DO throw `AttestryAPIError`. Mirror of
|
|
485
|
+
* `decisions.bulk`'s contract.
|
|
486
|
+
*
|
|
487
|
+
* **Multi-permission UNION auth scope (WRITE-side)**: kernel uses
|
|
488
|
+
* `requireApiKeyWithPermission(req, CLASSIFY, WRITE_ASSESSMENTS)`
|
|
489
|
+
* — OR semantics (`Array.some()` at `permissions.ts:53-55`).
|
|
490
|
+
* **First SDK route to use a WRITE-side union pair** (every prior
|
|
491
|
+
* SDK union has been READ-side). A key with EITHER permission
|
|
492
|
+
* (or `ADMIN`, or null/empty permissions for backwards-compat)
|
|
493
|
+
* succeeds. **HTTP 401** for no/invalid API key; **HTTP 403** for
|
|
494
|
+
* an authenticated key that has NEITHER required permission. Pin
|
|
495
|
+
* BOTH branches separately. Invariant #45 / #54.
|
|
496
|
+
*
|
|
497
|
+
* **NEW plan-guard 403 surface** (invariant candidate #55).
|
|
498
|
+
* **BEFORE Zod body parsing**, the kernel calls
|
|
499
|
+
* `requirePlan(org, "hasBatchProcessing")` at route.ts:67. A
|
|
500
|
+
* free-tier (or trial-expired non-enterprise) org hits the plan
|
|
501
|
+
* gate FIRST, regardless of body validity or systemIds. The
|
|
502
|
+
* kernel emits `PlanLimitError` which the route catches at line
|
|
503
|
+
* 216 and surfaces as **403** with a literal message of the form
|
|
504
|
+
* `'The "hasBatchProcessing" feature is not available on your
|
|
505
|
+
* current plan (<plan>). Please upgrade to access this feature.'`
|
|
506
|
+
* Distinct from the permission-403 message
|
|
507
|
+
* `'API key lacks required permission. Required: ... Key has: ...'`
|
|
508
|
+
* — consumers regex-match the message contents if they need to
|
|
509
|
+
* distinguish "upgrade your plan" from "grant more permissions to
|
|
510
|
+
* your key". Both surface uniformly as `AttestryAPIError(403)`
|
|
511
|
+
* (no SDK-side discriminator helper today).
|
|
512
|
+
*
|
|
513
|
+
* **Asymmetric cross-org / not-found error code (404 with
|
|
514
|
+
* EMBEDDED IDs)**: the kernel verifies every requested system
|
|
515
|
+
* belongs to the caller's org and collapses cross-org OR missing
|
|
516
|
+
* to 404. **NEW shape vs gate's literal 404** — the message
|
|
517
|
+
* embeds the comma-joined invalid UUIDs:
|
|
518
|
+
* `'Systems not found or not in your organization: <id1>, <id2>, ...'`.
|
|
519
|
+
* The SDK does NOT parse the embedded IDs — faithful courier;
|
|
520
|
+
* consumers can regex-match if they want to surface specific IDs
|
|
521
|
+
* to users.
|
|
522
|
+
*
|
|
523
|
+
* **TWO silent kernel-side truncations** (invariant candidate
|
|
524
|
+
* #50):
|
|
525
|
+
* 1. `orgSystems` row-population — `.limit(500)` at route.ts:76.
|
|
526
|
+
* The kernel reads up to 500 systems from the caller's org
|
|
527
|
+
* to verify membership. Orgs with >500 systems may see
|
|
528
|
+
* spurious 404s on batch submissions referencing systems
|
|
529
|
+
* outside the first 500 rows. **Documented kernel surface
|
|
530
|
+
* gap**; the SDK does NOT mask. Pin anchored to
|
|
531
|
+
* `.from(schema.aiSystems)[\s\S]*?.limit(500)` in the spec-
|
|
532
|
+
* diff round.
|
|
533
|
+
* 2. (GET-side `.limit(1)` is documented under `get()` below.)
|
|
534
|
+
*
|
|
535
|
+
* **`writeAuditLog` side effect** — every successful `submit()`
|
|
536
|
+
* call writes one `batch.submitted` entry to the org's audit log
|
|
537
|
+
* (route.ts:182-195). Properties of the write:
|
|
538
|
+
* - Org-scoped, hash-chained (per `writeAuditLog` at
|
|
539
|
+
* `src/lib/api.ts:125-`).
|
|
540
|
+
* - **Time-blocking** but error-tolerant: the kernel uses
|
|
541
|
+
* `await writeAuditLog(...)`, which awaits two DB ops (SELECT
|
|
542
|
+
* previous-hash + INSERT new entry). The submit-call response
|
|
543
|
+
* latency INCLUDES the audit-log write time. Error semantics
|
|
544
|
+
* ARE non-blocking: `writeAuditLog` wraps its body in a
|
|
545
|
+
* try/catch that swallows errors and logs them, so a write
|
|
546
|
+
* FAILURE does NOT fail the submit request.
|
|
547
|
+
* - NOT counted against `decisionsPerMonth` quota.
|
|
548
|
+
*
|
|
549
|
+
* **Closed-enum input `jobType`** — pre-rejected SDK-side if not
|
|
550
|
+
* one of `BATCH_JOB_TYPES`. Use `BATCH_JOB_TYPES` to iterate or
|
|
551
|
+
* narrow at call sites.
|
|
552
|
+
*
|
|
553
|
+
* **Per-row discriminator** — use `row.status === "success"` (NOT
|
|
554
|
+
* `row.errorMessage === undefined`, which is prototype-pollution
|
|
555
|
+
* unsafe). See `BatchSystemResult` JSDoc for the full rationale.
|
|
556
|
+
*
|
|
557
|
+
* Errors — ordered by kernel firing precedence (rate-limit →
|
|
558
|
+
* auth → org-load (404 if missing) → plan-guard → Zod body
|
|
559
|
+
* validation → DB membership check → inline processing →
|
|
560
|
+
* internal). A request with multiple problems surfaces ONLY the
|
|
561
|
+
* highest-precedence one. For example: a request with bad auth
|
|
562
|
+
* AND a free-tier org surfaces 401, not 403 (plan-guard fires
|
|
563
|
+
* AFTER auth). A request whose org row was deleted between key
|
|
564
|
+
* issuance and request time surfaces a 404 BEFORE the plan-
|
|
565
|
+
* guard 403 fires (rare in practice). A request with valid auth
|
|
566
|
+
* + valid org + free-tier + a malformed body surfaces 403
|
|
567
|
+
* (plan-guard fires BEFORE Zod body validation).
|
|
568
|
+
*
|
|
569
|
+
* - `AttestryAPIError` (status 429) — rate limit FIRES FIRST
|
|
570
|
+
* (auto-retried by default — invariant #18; per-IP rate-limit
|
|
571
|
+
* key `v1-batch:${ip}`; tighter limiter than apiLimiter —
|
|
572
|
+
* `assessmentLimiter` at 30 req/min vs apiLimiter's 60).
|
|
573
|
+
* - `AttestryAPIError` (status 401) — no API key OR invalid key.
|
|
574
|
+
* - `AttestryAPIError` (status 403, permission branch) — key
|
|
575
|
+
* has NEITHER `CLASSIFY` nor `WRITE_ASSESSMENTS`.
|
|
576
|
+
* - `AttestryAPIError` (status 404, org-not-found branch) — the
|
|
577
|
+
* caller's org row was deleted (route.ts:66: `if (!org)
|
|
578
|
+
* return errorResponse("Organization not found", 404)`).
|
|
579
|
+
* **Distinct from the systems-not-found 404 below** — same
|
|
580
|
+
* status code, different message. Rare in practice (orgs
|
|
581
|
+
* aren't deleted while keys are active).
|
|
582
|
+
* - `AttestryAPIError` (status 403, plan-gate branch) — the
|
|
583
|
+
* org's effective plan doesn't have `hasBatchProcessing`.
|
|
584
|
+
* Distinct wording from the permission-403 (above) — consumers
|
|
585
|
+
* regex-match to distinguish.
|
|
586
|
+
* - `AttestryAPIError` (status 422) — Zod schema rejection
|
|
587
|
+
* (kernel's `BodyParseError` surface — `parseBody(request,
|
|
588
|
+
* batchSubmitSchema)` failed). `apiErr.details` carries the
|
|
589
|
+
* full kernel error body verbatim (the transport does NOT
|
|
590
|
+
* strip the `{success:false, ...}` envelope on error
|
|
591
|
+
* responses — only the `{success:true, data}` envelope on
|
|
592
|
+
* success). The wire shape is: `{success: false, error:
|
|
593
|
+
* "Validation failed.", details: Array<{path: string;
|
|
594
|
+
* message: string}>}` — `error` is the literal string
|
|
595
|
+
* "Validation failed." (with trailing period), `details` is
|
|
596
|
+
* an array (NOT a keyed map) of `{path, message}` pairs
|
|
597
|
+
* derived from Zod's `result.error.errors`. Consumers reading
|
|
598
|
+
* field-by-field errors should iterate `apiErr.details.details`
|
|
599
|
+
* (the kernel's `details` array nested under the SDK's parsed-
|
|
600
|
+
* body wrapper). **The SDK pre-validates all closed-spec
|
|
601
|
+
* rules** (jobType enum membership, systemIds array length
|
|
602
|
+
* [1, 50] + per-element UUID format, frameworks array length
|
|
603
|
+
* ≤20 + per-element string length [1, 100]) AND the runtime
|
|
604
|
+
* checks always run regardless of TypeScript types — `as any`
|
|
605
|
+
* casts do NOT bypass them. So 422 reaches consumers ONLY
|
|
606
|
+
* via kernel rule changes the SDK hasn't synced to. Invariant
|
|
607
|
+
* #51.
|
|
608
|
+
* - `AttestryAPIError` (status 404, systems-not-found branch) —
|
|
609
|
+
* one or more `systemIds[i]` are not in the caller's org (or
|
|
610
|
+
* don't exist). The kernel collapses cross-org and genuine-
|
|
611
|
+
* missing to 404 with the literal message
|
|
612
|
+
* `"Systems not found or not in your organization: <id1>, <id2>, ..."`.
|
|
613
|
+
* **Embedded IDs** — the SDK does NOT parse the offending UUIDs
|
|
614
|
+
* out of the message; consumers can regex-match if needed.
|
|
615
|
+
* - `AttestryAPIError` (status 500) — internal kernel error
|
|
616
|
+
* (scrubbed message via `internalErrorResponse`).
|
|
617
|
+
* - `AttestryError` ("request aborted by caller") — caller-
|
|
618
|
+
* supplied `options.signal` fired (pre-aborted or mid-flight).
|
|
619
|
+
* - `AttestryError` (P2 hardening) — kernel response failed
|
|
620
|
+
* SDK-side shape validation (not an object, wrong type on
|
|
621
|
+
* any of the 10 response fields).
|
|
622
|
+
* - `AttestryAPIError` (P3 hardening) — kernel response had a
|
|
623
|
+
* wrong Content-Type (transport-level guard before body
|
|
624
|
+
* parsing).
|
|
625
|
+
* - `TypeError` — input failed SDK-side validation (null /
|
|
626
|
+
* array / non-object input, missing jobType, unknown
|
|
627
|
+
* jobType, missing systemIds, non-array systemIds, empty
|
|
628
|
+
* systemIds, oversize systemIds, non-string systemIds
|
|
629
|
+
* element, non-UUID systemIds element, non-object config,
|
|
630
|
+
* non-array config.frameworks, oversize config.frameworks,
|
|
631
|
+
* non-string config.frameworks element, oversize/empty
|
|
632
|
+
* config.frameworks element). **THROWN SYNCHRONOUSLY** (no
|
|
633
|
+
* fetch issued; the function does NOT return a promise in
|
|
634
|
+
* this case). Distinct from `AttestryAPIError` /
|
|
635
|
+
* `AttestryError` above, which reject through the returned
|
|
636
|
+
* promise. Consumers using `await client.batch.submit(...)`
|
|
637
|
+
* see both surfaces uniformly; consumers wrapping the call
|
|
638
|
+
* in a non-awaiting context (e.g., `client.batch.submit(...)
|
|
639
|
+
* .then(...)`) must catch the synchronous throw with a
|
|
640
|
+
* surrounding try/catch — the `.then()` chain alone does
|
|
641
|
+
* NOT catch synchronous TypeErrors.
|
|
642
|
+
*
|
|
643
|
+
* **Notably ABSENT**:
|
|
644
|
+
* - **No 400** on POST — all input validation is Zod (422).
|
|
645
|
+
* The GET method DOES have a 400 (malformed `id` path
|
|
646
|
+
* parameter).
|
|
647
|
+
* - **No 413** — body size limit not explicit.
|
|
648
|
+
* - **No 402** — read-shaped from a quota perspective (despite
|
|
649
|
+
* writing per-system classifications, doesn't count against
|
|
650
|
+
* `decisionsPerMonth`).
|
|
651
|
+
*
|
|
652
|
+
* **SDK-side validation** (synchronous `TypeError`, no fetch
|
|
653
|
+
* issued):
|
|
654
|
+
* - `input` itself: required; must be a non-null, non-array
|
|
655
|
+
* object.
|
|
656
|
+
* - `input.jobType`: required own-property; must be a string;
|
|
657
|
+
* must be one of `BATCH_JOB_TYPES`.
|
|
658
|
+
* - `input.systemIds`: required own-property; must be an Array;
|
|
659
|
+
* length [1, 50] inclusive; each element a non-empty string
|
|
660
|
+
* matching `UUID_REGEX`. Snapshot via `Array.from` for TOCTOU
|
|
661
|
+
* defense.
|
|
662
|
+
* - `input.config` (when own-property present, value not
|
|
663
|
+
* undefined): must be a non-null non-array object.
|
|
664
|
+
* - `input.config.frameworks` (when own-property present, value
|
|
665
|
+
* not undefined): must be an array of ≤20 strings, each of
|
|
666
|
+
* length 1-100. Snapshot via `Array.from` for TOCTOU defense.
|
|
667
|
+
*
|
|
668
|
+
* **Response-shape validation** (P2 hardening — symmetric defense
|
|
669
|
+
* on response side, mirror of session-16 second-hostile-review
|
|
670
|
+
* MEDIUM #3 carry-forward):
|
|
671
|
+
* - Rejects with `AttestryError` if the kernel response isn't a
|
|
672
|
+
* non-null, non-array object.
|
|
673
|
+
* - Rejects if `id` / `jobType` / `status` / `createdAt` /
|
|
674
|
+
* `completedAt` aren't strings.
|
|
675
|
+
* - Rejects if `startedAt` isn't a string or null.
|
|
676
|
+
* - Rejects if `totalSystems` / `processedSystems` /
|
|
677
|
+
* `failedSystems` aren't numbers.
|
|
678
|
+
* - Rejects if `results` isn't an array.
|
|
679
|
+
* - Per-row shape (open-spec — `BatchSystemResult`) is faithful-
|
|
680
|
+
* courier — NOT validated.
|
|
681
|
+
* - Each response field read goes through the module-load
|
|
682
|
+
* `objectHasOwn` snapshot (symmetric to input-side defense).
|
|
683
|
+
*
|
|
684
|
+
* **Transport-shape validation** (P3 hardening):
|
|
685
|
+
* - Rejects with `AttestryAPIError` if the kernel responds with
|
|
686
|
+
* a non-`application/json` Content-Type.
|
|
687
|
+
*
|
|
688
|
+
* @example Submit a classify job for 3 systems
|
|
689
|
+
* ```ts
|
|
690
|
+
* const result = await client.batch.submit({
|
|
691
|
+
* jobType: "classify",
|
|
692
|
+
* systemIds: [
|
|
693
|
+
* "11111111-1111-1111-1111-111111111111",
|
|
694
|
+
* "22222222-2222-2222-2222-222222222222",
|
|
695
|
+
* "33333333-3333-3333-3333-333333333333",
|
|
696
|
+
* ],
|
|
697
|
+
* });
|
|
698
|
+
* console.log(`Processed ${result.processedSystems}/${result.totalSystems}`);
|
|
699
|
+
* for (const row of result.results) {
|
|
700
|
+
* if (row.status === "success") {
|
|
701
|
+
* console.log(`OK ${row.systemId}:`, row.classifications);
|
|
702
|
+
* } else {
|
|
703
|
+
* // CRITICAL: branch on `row.status === "error"` — NOT
|
|
704
|
+
* // `row.errorMessage === undefined` (prototype-pollution
|
|
705
|
+
* // unsafe).
|
|
706
|
+
* console.error(`FAIL ${row.systemId}: ${row.errorMessage}`);
|
|
707
|
+
* }
|
|
708
|
+
* }
|
|
709
|
+
* ```
|
|
710
|
+
*
|
|
711
|
+
* @example Submit with framework filter (round-trip only today)
|
|
712
|
+
* ```ts
|
|
713
|
+
* const job = await client.batch.submit({
|
|
714
|
+
* jobType: "classify_and_assess",
|
|
715
|
+
* systemIds: ["11111111-1111-1111-1111-111111111111"],
|
|
716
|
+
* config: { frameworks: ["EU_AI_ACT", "ISO_42001"] },
|
|
717
|
+
* });
|
|
718
|
+
* // config.frameworks is persisted on the row but has no effect on
|
|
719
|
+
* // the current inline classification path.
|
|
720
|
+
* ```
|
|
721
|
+
*/
|
|
722
|
+
submit(input: BatchSubmitInput, options?: RequestOptions): Promise<BatchSubmitResponse>;
|
|
723
|
+
/**
|
|
724
|
+
* Retrieve a batch job's status and results by UUID. Returns a
|
|
725
|
+
* `BatchJobStatus` with the wider 4-value `status` enum (NOT the
|
|
726
|
+
* narrower `"completed" | "failed"` of POST) plus the original
|
|
727
|
+
* `config` (round-tripped from submission).
|
|
728
|
+
*
|
|
729
|
+
* **Single-permission auth scope (DIFFERENT from `submit()`)** —
|
|
730
|
+
* kernel uses `requireApiKeyWithPermission(req, READ_ASSESSMENTS)`
|
|
731
|
+
* with ONLY ONE required permission, NOT a union. Status reads
|
|
732
|
+
* don't need `CLASSIFY` or `WRITE_ASSESSMENTS`. **First SDK
|
|
733
|
+
* resource with asymmetric auth between methods on the same
|
|
734
|
+
* resource** (invariant candidate #54). Pin BOTH 401 (no/invalid
|
|
735
|
+
* key) AND 403 (key lacks `READ_ASSESSMENTS`) branches.
|
|
736
|
+
*
|
|
737
|
+
* **NO plan-guard surface on `get()`** — `requirePlan` is invoked
|
|
738
|
+
* only in `submit()`. A free-tier org can `get()` a job that was
|
|
739
|
+
* previously submitted (e.g., on a higher plan that has since
|
|
740
|
+
* downgraded). The submission would have been gated; the read
|
|
741
|
+
* isn't.
|
|
742
|
+
*
|
|
743
|
+
* **400 surface on malformed UUID path parameter** — the kernel's
|
|
744
|
+
* `isValidUuid(id)` check at route.ts:36 returns false →
|
|
745
|
+
* `errorResponse("Invalid batch job ID format", 400)`. The SDK
|
|
746
|
+
* pre-validates the UUID format synchronously (`TypeError`) — so
|
|
747
|
+
* the 400 reaches consumers only via `as any` casts or a kernel-
|
|
748
|
+
* side switch to a different UUID flavor (ULID, KSUID, etc.).
|
|
749
|
+
*
|
|
750
|
+
* **404 surface (literal string)** — the kernel's `where(id +
|
|
751
|
+
* orgId)` query returns zero rows → `errorResponse("Batch job not
|
|
752
|
+
* found", 404)`. **NEW shape** vs `submit()`'s 404 with embedded
|
|
753
|
+
* IDs — the `get()` 404 is a LITERAL string with no variable
|
|
754
|
+
* data. Cross-org `id` collapses to the same 404 (the `eq(orgId,
|
|
755
|
+
* apiKeyUser.orgId)` clause silently filters out matching IDs in
|
|
756
|
+
* other orgs).
|
|
757
|
+
*
|
|
758
|
+
* **No `writeAuditLog` side effect on `get()`** — status reads
|
|
759
|
+
* are quiet. Asymmetric with `submit()`'s `batch.submitted`
|
|
760
|
+
* write.
|
|
761
|
+
*
|
|
762
|
+
* **Defensive `.limit(1)` on the batchJobs query** (route.ts:49)
|
|
763
|
+
* — the `where` clause already narrows to one row by primary-key
|
|
764
|
+
* UUID, so this cap is belt-and-suspenders against a hypothetical
|
|
765
|
+
* future schema change (composite primary key, soft-deleted-rows
|
|
766
|
+
* union). Pin separately as a kernel surface gap. Invariant
|
|
767
|
+
* candidate #50.
|
|
768
|
+
*
|
|
769
|
+
* Errors — ordered by kernel firing precedence (rate-limit →
|
|
770
|
+
* auth → UUID format → DB lookup → internal):
|
|
771
|
+
* - `AttestryAPIError` (status 429) — rate limit (auto-retried;
|
|
772
|
+
* per-IP key `v1-batch-status:${ip}`; uses the standard
|
|
773
|
+
* `apiLimiter` at 60 req/min — looser than `submit()`'s 30/
|
|
774
|
+
* min `assessmentLimiter`).
|
|
775
|
+
* - `AttestryAPIError` (status 401) — no API key OR invalid key.
|
|
776
|
+
* - `AttestryAPIError` (status 403) — authenticated key lacks
|
|
777
|
+
* `READ_ASSESSMENTS` permission (single-permission check,
|
|
778
|
+
* NOT a union). Pin separately from `submit()`'s 403.
|
|
779
|
+
* - `AttestryAPIError` (status 400) — kernel's `isValidUuid(id)`
|
|
780
|
+
* returned false. **The SDK pre-validates UUID format**, so
|
|
781
|
+
* this 400 reaches consumers ONLY via `as any` casts or
|
|
782
|
+
* kernel-side UUID flavor changes.
|
|
783
|
+
* - `AttestryAPIError` (status 404) — batch job not found OR
|
|
784
|
+
* cross-org `id` (kernel collapses to "Batch job not found";
|
|
785
|
+
* literal string with NO embedded variable data — distinct
|
|
786
|
+
* from `submit()`'s 404 shape).
|
|
787
|
+
* - `AttestryAPIError` (status 500) — internal kernel error
|
|
788
|
+
* (scrubbed message).
|
|
789
|
+
* - `AttestryError` ("request aborted by caller") — caller-
|
|
790
|
+
* supplied `options.signal` fired.
|
|
791
|
+
* - `AttestryError` (P2 hardening) — kernel response failed
|
|
792
|
+
* SDK-side shape validation (11 fields).
|
|
793
|
+
* - `AttestryAPIError` (P3 hardening) — wrong Content-Type.
|
|
794
|
+
* - `TypeError` — input failed SDK-side validation (missing
|
|
795
|
+
* `id`, non-string `id`, empty `id`, malformed UUID `id`).
|
|
796
|
+
* **THROWN SYNCHRONOUSLY** (no fetch issued; not via the
|
|
797
|
+
* returned promise). Consumers using `.then(...)` without
|
|
798
|
+
* a surrounding try/catch see this surface as an uncaught
|
|
799
|
+
* synchronous throw — same caveat as `submit()`.
|
|
800
|
+
*
|
|
801
|
+
* **SDK-side validation** (synchronous `TypeError`, no fetch
|
|
802
|
+
* issued):
|
|
803
|
+
* - `id`: required; must be a non-empty string matching
|
|
804
|
+
* `UUID_REGEX` (RFC 4122 hyphenated, case-insensitive).
|
|
805
|
+
*
|
|
806
|
+
* **Response-shape validation** (P2 hardening — 11 fields, all
|
|
807
|
+
* always-present; symmetric defense on response side via the
|
|
808
|
+
* module-load `objectHasOwn` snapshot):
|
|
809
|
+
* - Rejects with `AttestryError` if the response isn't a non-
|
|
810
|
+
* null, non-array object.
|
|
811
|
+
* - Rejects if `id` / `jobType` / `status` / `createdAt` aren't
|
|
812
|
+
* strings.
|
|
813
|
+
* - Rejects if `totalSystems` / `processedSystems` /
|
|
814
|
+
* `failedSystems` aren't numbers.
|
|
815
|
+
* - Rejects if `results` isn't `null` OR an array.
|
|
816
|
+
* - Rejects if `config` isn't `null` OR a non-array object.
|
|
817
|
+
* - Rejects if `startedAt` / `completedAt` aren't `null` OR
|
|
818
|
+
* a string.
|
|
819
|
+
* - Per-row shape (open-spec `BatchSystemResult`) is faithful-
|
|
820
|
+
* courier — NOT validated.
|
|
821
|
+
* - `config.frameworks` shape is faithful-courier — NOT
|
|
822
|
+
* validated.
|
|
823
|
+
*
|
|
824
|
+
* **NO URIError defense on the `id` path segment** — the SDK
|
|
825
|
+
* pre-validates the UUID format (synchronous `TypeError`) BEFORE
|
|
826
|
+
* constructing the URL. A lone-surrogate or non-hex `id` is
|
|
827
|
+
* rejected before any `encodeURIComponent`-style call could fire.
|
|
828
|
+
* Hex-only UUIDs are guaranteed-safe for path concatenation.
|
|
829
|
+
*
|
|
830
|
+
* @example Poll a job's status
|
|
831
|
+
* ```ts
|
|
832
|
+
* const job = await client.batch.get("11111111-1111-1111-1111-111111111111");
|
|
833
|
+
* if (job.status === "completed") {
|
|
834
|
+
* console.log(`Processed ${job.processedSystems}/${job.totalSystems}`);
|
|
835
|
+
* } else if (job.status === "failed") {
|
|
836
|
+
* console.error("Batch failed entirely");
|
|
837
|
+
* } else {
|
|
838
|
+
* // "pending" / "processing" — still in flight
|
|
839
|
+
* console.log(`Job is ${job.status}`);
|
|
840
|
+
* }
|
|
841
|
+
* ```
|
|
842
|
+
*/
|
|
843
|
+
get(id: string, options?: RequestOptions): Promise<BatchJobStatus>;
|
|
844
|
+
}
|
|
845
|
+
//# sourceMappingURL=batch.d.ts.map
|