@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,402 @@
1
+ // ─── ComplianceCheck resource ───────────────────────────────────────────────
2
+ //
3
+ // Wraps the compliance-check surface (session 15):
4
+ //
5
+ // - GET /api/v1/compliance-check?systemId=<UUID>
6
+ // - GET /api/v1/compliance-check?orgName=<string>
7
+ //
8
+ // Third non-decisions resource on `@attestry/sdk`. Sibling to
9
+ // `IncidentsResource`, `DecisionsResource`, `ChatResource`,
10
+ // `AuditLogResource`, `RegulatoryChangesResource`. Single public
11
+ // method today (`check`); the resource class exists as the landing
12
+ // pad for future compliance-check methods if/when the kernel adds
13
+ // them (resource-class-per-kernel-resource convention, carry-forward
14
+ // invariant candidate #43).
15
+ //
16
+ // Multi-permission UNION auth scope: the kernel route gates on
17
+ // `requireApiKeyWithPermission(request, READ_SYSTEMS, READ_ASSESSMENTS)`
18
+ // which is OR semantics — `permissions.ts:53-55` uses `Array.some()`,
19
+ // NOT `.every()`. A key with EITHER permission (or `ADMIN`, or empty
20
+ // permissions for backwards-compat) succeeds. **HTTP 401** for
21
+ // no/invalid API key (the `requireApiKey` branch fires first inside
22
+ // `requireApiKeyWithPermission`); **HTTP 403** for an authenticated
23
+ // key that has NEITHER required permission. Pin BOTH branches
24
+ // separately. **Different from `auditLog.export`** which returns 401
25
+ // for both unauth and insufficient-permission (ADMIN-only convention;
26
+ // carry-forward invariant #42 governs ADMIN routes specifically).
27
+ // First SDK route to exercise the multi-arg form of
28
+ // `requireApiKeyWithPermission` — invariant candidate #45.
29
+ //
30
+ // XOR input mode (the **first** non-obvious gotcha): exactly one of
31
+ // `systemId` OR `orgName` must be provided. The kernel is NOT strictly
32
+ // XOR — when both are provided, kernel uses `systemId` and silently
33
+ // ignores `orgName` (route.ts:80-87). The SDK is **stricter** than the
34
+ // kernel — synchronously rejects "both provided" with a clear
35
+ // `TypeError`. This protects consumers from silent shadow-of-orgName
36
+ // bugs that the kernel quietly enables. Invariant candidate #46.
37
+ //
38
+ // Asymmetric cross-org error codes (the **second** non-obvious
39
+ // gotcha): cross-org systemId returns **404** (kernel collapses to
40
+ // "System not found", route.ts:76 — mirror of decisions.retrieve),
41
+ // while cross-org orgName returns **403** ("Access denied",
42
+ // route.ts:95). Pin BOTH branches and document the asymmetry in
43
+ // JSDoc + README. Invariant candidate #47.
44
+ //
45
+ // Silent `.limit(100)` on orgName path (the **third** non-obvious
46
+ // gotcha): route.ts:107 hardcodes `.limit(100)` on the org-systems
47
+ // query. Orgs with more than 100 systems see a truncated set with NO
48
+ // indicator — no `total` field, no `hasMore` cursor. The SDK does NOT
49
+ // mask this — JSDoc + README call it out as a kernel surface gap.
50
+ // Faithful-courier policy.
51
+ //
52
+ // Sync JSON request/response: reuses `client._request` and the
53
+ // existing `{success:true, data}` envelope-unwrap (carry-forward
54
+ // invariant #9). NO new SDK primitive needed — smaller blast radius
55
+ // than `auditLog.export`. Returns
56
+ // `Promise<ComplianceCheckResponse>`.
57
+ import { AttestryError } from "../errors.js";
58
+ import { readInputField } from "./safe-input-read.js";
59
+ // Module-load snapshot of `Object.hasOwn` — defends against a
60
+ // late-loading hostile/buggy npm dependency that overrides the global
61
+ // (e.g., `Object.hasOwn = () => true`). Without the snapshot, the H4
62
+ // prototype-pollution defense uses whatever Object.hasOwn the
63
+ // dependency replaced it with at request time. Snapshotting at module
64
+ // load captures the original implementation BEFORE most consumer
65
+ // code has a chance to monkey-patch.
66
+ //
67
+ // Caveat: this is partial. If the hostile dependency is imported
68
+ // BEFORE @attestry/sdk in the consumer's load graph, the snapshot
69
+ // captures the bad version. Consumers ordering imports
70
+ // SDK-then-untrusted-deps benefit; the reverse ordering does not.
71
+ // Combined with `Object.hasOwn` itself being immune to
72
+ // `obj.hasOwnProperty = ...` overrides (per MDN), this gives a
73
+ // layered defense — perfect protection isn't possible at the SDK
74
+ // layer.
75
+ //
76
+ // Hostile review session 15 finding (LOW #2).
77
+ const objectHasOwn = Object.hasOwn;
78
+ /**
79
+ * ComplianceCheck resource — sibling to `IncidentsResource`,
80
+ * `DecisionsResource`, `ChatResource`, `AuditLogResource`,
81
+ * `RegulatoryChangesResource`. Today wraps a single endpoint
82
+ * (`check`); the class is the landing pad for future compliance-check
83
+ * methods if the kernel adds them (resource-class-per-kernel-resource
84
+ * convention, invariant candidate #43).
85
+ */
86
+ export class ComplianceCheckResource {
87
+ client;
88
+ constructor(client) {
89
+ this.client = client;
90
+ }
91
+ /**
92
+ * Return a per-system compliance summary for either a single system
93
+ * (by UUID) or every system in an org (by org name, capped at 100).
94
+ * Returns `Promise<ComplianceCheckResponse>` — sync JSON, no
95
+ * pagination, no streaming.
96
+ *
97
+ * **Input mode — XOR with kernel quirk** (read carefully): exactly
98
+ * one of `systemId` OR `orgName` must be provided. The kernel is
99
+ * NOT strictly XOR — when both are provided, kernel silently picks
100
+ * `systemId` and ignores `orgName`. The SDK is **stricter** than
101
+ * the kernel and synchronously throws `TypeError` when both are
102
+ * provided. This is a deliberate D3 deviation: the kernel's
103
+ * silent-pick is a quirk that future maintenance could change at
104
+ * any time, and surfacing the conflict at the SDK boundary makes
105
+ * consumer code stable across kernel revisions.
106
+ *
107
+ * **Multi-permission UNION auth scope**: kernel uses
108
+ * `requireApiKeyWithPermission(req, READ_SYSTEMS, READ_ASSESSMENTS)`
109
+ * which is OR semantics (`Array.some()` at
110
+ * `permissions.ts:53-55`). A key with EITHER permission (or `ADMIN`,
111
+ * or null/empty permissions for backwards-compat) succeeds.
112
+ * **HTTP 401** for no/invalid API key, **HTTP 403** for an
113
+ * authenticated key that has NEITHER required permission. Pin
114
+ * BOTH branches separately. **Distinct from `auditLog.export`** in
115
+ * the auth MODEL — that route is ADMIN-only dual-auth — but NOT in
116
+ * the status surface: `auditLog.export` also returns 401 vs 403
117
+ * distinctly (corrected session-22 hostile review #2; the prior
118
+ * "ADMIN-only 401-for-both" framing of invariant #42 was wrong).
119
+ * First SDK route to exercise multi-arg
120
+ * `requireApiKeyWithPermission` — invariant candidate #45.
121
+ *
122
+ * **Asymmetric cross-org error codes** (read carefully):
123
+ * cross-org `systemId` returns **404** (kernel collapses to
124
+ * "System not found" at route.ts:76 — mirror of
125
+ * `decisions.retrieve`); cross-org `orgName` returns **403**
126
+ * ("Access denied" at route.ts:95). Consumers writing defensive
127
+ * error-handling logic must distinguish: a 404 on systemId path
128
+ * may be "not your org" OR "genuine missing UUID"; a 403 on
129
+ * orgName path is unambiguously "the org exists but you don't
130
+ * own it". Invariant candidate #47.
131
+ *
132
+ * **Silent `.limit(100)` on orgName path** (read carefully): if
133
+ * the org has more than 100 systems, the response is silently
134
+ * truncated to the first 100 — NO `total` field, NO `hasMore`
135
+ * cursor, NO warning. The SDK does NOT mask this (faithful
136
+ * courier — kernel decided 100 is enough). Consumers managing
137
+ * >100-system orgs should switch to systemId-per-row.
138
+ *
139
+ * **`compliant` field — implicit threshold of 70**:
140
+ * `compliant === activeAttestations > 0 && (overallScore === null || overallScore >= 70)`.
141
+ * Documented in detail on `ComplianceCheckResult.compliant` —
142
+ * consumers wanting a different bar can apply it post-hoc via
143
+ * the `score` field.
144
+ *
145
+ * Errors (kernel firing precedence: rate-limit → auth → input
146
+ * validation, so a request with multiple problems surfaces only the
147
+ * highest-precedence one. Hostile-review LOW #8):
148
+ * - `AttestryAPIError` (status 429) — rate limit FIRES FIRST
149
+ * (auto-retried by default — invariant #18; per-IP rate-limit
150
+ * key `v1-compliance-check:${ip}`). A flooded IP gets 429 even
151
+ * for unauthenticated or malformed requests.
152
+ * - `AttestryAPIError` (status 401) — no API key OR invalid key
153
+ * (the `requireApiKey` branch). Fires AFTER rate-limit but
154
+ * BEFORE input validation.
155
+ * - `AttestryAPIError` (status 403) — authenticated key has
156
+ * NEITHER `READ_SYSTEMS` nor `READ_ASSESSMENTS` (the
157
+ * permission-check branch); OR cross-org orgName ("Access
158
+ * denied"). Distinguish via the response body's `error`
159
+ * message.
160
+ * - `AttestryAPIError` (status 400) — invalid systemId UUID
161
+ * format (kernel's `isValidUuid` rejection). Fires AFTER auth.
162
+ * The SDK does NOT pre-validate UUID format (D2: kernel is the
163
+ * authority). The "neither systemId nor orgName" 400 is
164
+ * UNREACHABLE through the SDK — pre-rejected as `TypeError`.
165
+ * - `AttestryAPIError` (status 404) — systemId not found OR
166
+ * systemId belongs to a different org (kernel collapses
167
+ * cross-org systemId to 404); OR orgName not found.
168
+ * - `AttestryAPIError` (status 500) — internal kernel error
169
+ * (scrubbed message via `internalErrorResponse`).
170
+ * - `AttestryError` ("request aborted by caller") — caller-supplied
171
+ * `options.signal` fired (pre-aborted or mid-flight).
172
+ * - `AttestryError` (P2 hardening) — kernel response failed
173
+ * SDK-side shape validation (not an object, missing `systems`
174
+ * array, missing `checkedAt` string).
175
+ * - `AttestryAPIError` (P3 hardening) — kernel response had a
176
+ * wrong Content-Type (transport-level guard before body parsing).
177
+ * - `TypeError` (synchronous, no fetch issued) — input failed
178
+ * SDK-side validation (null / array / non-object input,
179
+ * neither systemId nor orgName provided, both provided, empty
180
+ * string, non-string, lone surrogates).
181
+ *
182
+ * **Notably ABSENT**:
183
+ * - **No 422** — no Zod schema; no closed enums in input.
184
+ * - **No 413** — no body size limit (no body — GET).
185
+ * - **No 402** — read-only, doesn't count against decisionsPerMonth quota.
186
+ *
187
+ * **SDK-side validation** (synchronous `TypeError`, no fetch
188
+ * issued):
189
+ * - `input` itself: required; must be a non-null, non-array
190
+ * object.
191
+ * - `input.systemId` XOR `input.orgName`: exactly one must be
192
+ * provided. Both = `TypeError` (stricter than kernel — D3).
193
+ * Neither = `TypeError`.
194
+ * - `input.systemId` (when provided): non-empty string.
195
+ * Lone-surrogate guard via `assertEncodableQueryString`
196
+ * (carry-forward invariant #32). UUID format NOT pre-validated
197
+ * (D2 — kernel is the authority).
198
+ * - `input.orgName` (when provided): non-empty string.
199
+ * Lone-surrogate guard.
200
+ *
201
+ * **Response-shape validation** (P2 hardening):
202
+ * - Rejects with `AttestryError` if the kernel response isn't a
203
+ * non-null, non-array object.
204
+ * - Rejects if `response.systems` isn't an array.
205
+ * - Rejects if `response.checkedAt` isn't a string.
206
+ * - Per-row shape (the 7-field `ComplianceCheckResult`) is
207
+ * faithful-courier — NOT validated (P4 candidate).
208
+ *
209
+ * **Transport-shape validation** (P3 hardening):
210
+ * - Rejects with `AttestryAPIError` if the kernel responds with
211
+ * a non-`application/json` Content-Type — protects against
212
+ * proxy-injected HTML 200 pages parsing into junk consumer
213
+ * state.
214
+ *
215
+ * @example Compliance check by system UUID
216
+ * ```ts
217
+ * const result = await client.complianceCheck.check({
218
+ * systemId: "11111111-1111-1111-1111-111111111111",
219
+ * });
220
+ * for (const system of result.systems) {
221
+ * console.log(system.systemName, system.compliant, system.score);
222
+ * }
223
+ * ```
224
+ *
225
+ * @example Compliance check by org name (capped at 100 systems)
226
+ * ```ts
227
+ * const result = await client.complianceCheck.check({
228
+ * orgName: "Acme Corp",
229
+ * });
230
+ * console.log(`${result.systems.length} systems checked at ${result.checkedAt}`);
231
+ * ```
232
+ */
233
+ check(input, options) {
234
+ // Top-level shape — input is REQUIRED (unlike auditLog.export /
235
+ // regulatoryChanges.list which accept undefined). typeof null ===
236
+ // "object" and typeof [] === "object", so guard both explicitly.
237
+ if (input === null ||
238
+ typeof input !== "object" ||
239
+ Array.isArray(input)) {
240
+ throw new TypeError("complianceCheck.check: `input` must be a non-null object with `systemId` or `orgName`");
241
+ }
242
+ // XOR check — snapshot each field's value EXACTLY ONCE up front,
243
+ // then operate only on the locals downstream. Three motivations:
244
+ // 1. **Prototype-pollution defense (hostile round H4)**:
245
+ // `Object.prototype.systemId = "evil-uuid"` (set somewhere
246
+ // else in the consumer's process) DOES NOT trick the SDK
247
+ // into thinking systemId was provided when the user passed
248
+ // `{orgName: "Acme"}`. `Object.hasOwn` only checks own
249
+ // properties (ES2022, Node 16.9+ — well below the SDK's
250
+ // Node 18 floor). Use the module-load snapshot
251
+ // (`objectHasOwn`) so a late-loading dep that overrides the
252
+ // global doesn't defeat the defense. Hostile-review LOW #2.
253
+ // 2. **TOCTOU defense (hostile-review LOW #1)**: a Proxy or
254
+ // getter-defining input could yield DIFFERENT values across
255
+ // multiple reads of the same field — the SDK would validate
256
+ // one value and send another to the wire. Snapshotting once
257
+ // collapses validate-then-send to a single read per field;
258
+ // the validated value is provably the value sent.
259
+ // 3. An explicit `{systemId: "uuid", orgName: undefined}` (which
260
+ // TS permits via the `orgName?: never` branch) is treated as
261
+ // systemId-only — `objectHasOwn` says "yes, orgName is an
262
+ // own property" but the value is `undefined`, so the
263
+ // undefined-check filters it out.
264
+ //
265
+ // Each input field is read exactly once via the indexer below;
266
+ // every subsequent operation uses the local snapshot.
267
+ const systemIdRaw = objectHasOwn(input, "systemId")
268
+ ? readInputField(input, "systemId", "complianceCheck.check")
269
+ : undefined;
270
+ const orgNameRaw = objectHasOwn(input, "orgName")
271
+ ? readInputField(input, "orgName", "complianceCheck.check")
272
+ : undefined;
273
+ const hasSystemId = systemIdRaw !== undefined;
274
+ const hasOrgName = orgNameRaw !== undefined;
275
+ if (!hasSystemId && !hasOrgName) {
276
+ throw new TypeError("complianceCheck.check: must provide exactly one of `systemId` or `orgName`");
277
+ }
278
+ if (hasSystemId && hasOrgName) {
279
+ // **SDK is STRICTER than the kernel**. The kernel silently
280
+ // picks systemId when both are provided (route.ts:80-87).
281
+ // The SDK rejects to prevent shadow-of-orgName bugs that
282
+ // could otherwise pass kernel-side and produce confusing
283
+ // results (consumer expects orgName to take effect; gets
284
+ // systemId-shaped response). D3 / invariant candidate #46.
285
+ throw new TypeError("complianceCheck.check: provide either `systemId` or `orgName`, not both " +
286
+ "(kernel silently ignores `orgName` when both are present; SDK rejects " +
287
+ "synchronously to prevent shadow-of-orgName bugs)");
288
+ }
289
+ let validatedSystemId;
290
+ let validatedOrgName;
291
+ if (hasSystemId) {
292
+ if (typeof systemIdRaw !== "string" || systemIdRaw.length === 0) {
293
+ throw new TypeError("complianceCheck.check: `systemId` must be a non-empty string when provided");
294
+ }
295
+ // UUID format NOT pre-validated (D2 — kernel's `isValidUuid`
296
+ // is the authority). Lone-surrogate guard (#32) catches the
297
+ // URIError defect class.
298
+ assertEncodableQueryString(systemIdRaw, "systemId", "complianceCheck.check");
299
+ validatedSystemId = systemIdRaw;
300
+ }
301
+ else {
302
+ if (typeof orgNameRaw !== "string" || orgNameRaw.length === 0) {
303
+ throw new TypeError("complianceCheck.check: `orgName` must be a non-empty string when provided");
304
+ }
305
+ assertEncodableQueryString(orgNameRaw, "orgName", "complianceCheck.check");
306
+ validatedOrgName = orgNameRaw;
307
+ }
308
+ return this.client
309
+ ._request({
310
+ method: "GET",
311
+ path: "/api/v1/compliance-check",
312
+ query: {
313
+ systemId: validatedSystemId,
314
+ orgName: validatedOrgName,
315
+ },
316
+ options,
317
+ })
318
+ .then((result) => {
319
+ // P2 hardening: validate the kernel returned an object with
320
+ // the expected top-level shape. The route emits
321
+ // `successResponse({systems, checkedAt})` where systems is
322
+ // always an array (initialized as `[]` and populated via
323
+ // `.push`) and checkedAt is always a string
324
+ // (`new Date().toISOString()`). A kernel-side regression that
325
+ // breaks any of these invariants would let a malformed shape
326
+ // reach consumers, who would crash on `result.systems.length`
327
+ // or similar with a confusing TypeError. Catch at the SDK
328
+ // boundary with a clear AttestryError.
329
+ if (result === null ||
330
+ typeof result !== "object" ||
331
+ Array.isArray(result)) {
332
+ throw new AttestryError(`complianceCheck.check: expected an object response from the kernel (got ${describeType(result)})`);
333
+ }
334
+ const obj = result;
335
+ if (!Array.isArray(obj.systems)) {
336
+ throw new AttestryError(`complianceCheck.check: expected response.systems to be an array (got ${describeType(obj.systems)})`);
337
+ }
338
+ if (typeof obj.checkedAt !== "string") {
339
+ throw new AttestryError(`complianceCheck.check: expected response.checkedAt to be a string (got ${describeType(obj.checkedAt)})`);
340
+ }
341
+ return result;
342
+ });
343
+ }
344
+ }
345
+ /**
346
+ * Synchronously verify a query-string value is encodable via
347
+ * `encodeURIComponent`. Mirrors the helper in `decisions.ts`,
348
+ * `audit-log.ts`, and `regulatory-changes.ts` (carry-forward invariant
349
+ * #32 — URIError defect-class is uniformly handled).
350
+ *
351
+ * Duplicated rather than shared because cross-resource imports between
352
+ * leaf-resource modules would create graph-cycle hazards. A future
353
+ * SDK refactor may extract validation helpers to a shared module
354
+ * (e.g., `src/validate.ts`) when a fifth caller shows up; for now the
355
+ * duplication is intentional and documented.
356
+ */
357
+ function assertEncodableQueryString(value, fieldName, methodName) {
358
+ try {
359
+ encodeURIComponent(value);
360
+ }
361
+ catch (err) {
362
+ throw new TypeError(`${methodName}: \`${fieldName}\` contains invalid UTF-16 sequences (${
363
+ // encodeURIComponent always throws URIError (an Error
364
+ // subclass), so the String(err) branch is unreachable.
365
+ // Defense-in-depth marker for the v8 coverage tool.
366
+ /* v8 ignore next */
367
+ err instanceof Error ? err.message : String(err)})`, { cause: err });
368
+ }
369
+ }
370
+ /**
371
+ * Human-readable type description for response-shape error messages.
372
+ * Distinguishes `null` and `array` from generic `object`.
373
+ *
374
+ * Duplicated in `decisions.ts`, `incidents.ts`, and
375
+ * `regulatory-changes.ts` per project pattern (small helper,
376
+ * leaf-resource modules, no shared module yet).
377
+ *
378
+ * In complianceCheck.check, `describeType` is invoked from THREE
379
+ * call sites:
380
+ * 1. Top-level "expected an object" error — value is null OR a
381
+ * non-object scalar OR an array (the negation of "non-null
382
+ * non-array object"). All three branches are reachable here.
383
+ * 2. "expected response.systems to be an array" — value is the
384
+ * `systems` field, which can be anything but `Array`. All
385
+ * branches reachable.
386
+ * 3. "expected response.checkedAt to be a string" — value is the
387
+ * `checkedAt` field, which can be anything but `string`. All
388
+ * branches reachable.
389
+ *
390
+ * Unlike regulatoryChanges.list (which only invokes describeType
391
+ * from the "not an array" path, making the array branch
392
+ * structurally unreachable in that call site), every branch here
393
+ * IS reachable. No v8-ignore-next marker needed on any branch.
394
+ */
395
+ function describeType(value) {
396
+ if (value === null)
397
+ return "null";
398
+ if (Array.isArray(value))
399
+ return "array";
400
+ return typeof value;
401
+ }
402
+ //# sourceMappingURL=compliance-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compliance-check.js","sourceRoot":"","sources":["../../src/resources/compliance-check.ts"],"names":[],"mappings":"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"}