@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,1034 @@
|
|
|
1
|
+
import type { AttestryClient } from "../client.js";
|
|
2
|
+
import type { RequestOptions } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Closed-enum of resources an ABAC policy can target. Runtime mirror
|
|
5
|
+
* of the kernel's `RESOURCES` const at `src/lib/auth.ts:29-40`.
|
|
6
|
+
* Object.freeze'd to prevent runtime mutation.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ABAC_POLICY_RESOURCES: readonly ["systems", "assessments", "documents", "attestations", "evidence", "users", "api_keys", "audit_log", "organization", "regulations"];
|
|
9
|
+
/**
|
|
10
|
+
* Closed-enum of actions an ABAC policy can gate. Runtime mirror of
|
|
11
|
+
* the kernel's `ACTIONS` const at `src/lib/auth.ts:42-48`.
|
|
12
|
+
*/
|
|
13
|
+
export declare const ABAC_POLICY_ACTIONS: readonly ["create", "read", "update", "delete", "manage"];
|
|
14
|
+
/**
|
|
15
|
+
* Closed-enum of effects. Runtime mirror of the kernel's `Effect` type
|
|
16
|
+
* alias at `src/lib/auth/abac-policies.ts:77` + the Zod
|
|
17
|
+
* `z.enum(["allow", "deny"]).default("allow")` at
|
|
18
|
+
* `src/lib/auth/abac-policies.ts:756`.
|
|
19
|
+
*/
|
|
20
|
+
export declare const ABAC_POLICY_EFFECTS: readonly ["allow", "deny"];
|
|
21
|
+
/**
|
|
22
|
+
* Stable enum of ABAC effects. Mirror of the kernel's `Effect` type
|
|
23
|
+
* alias at `src/lib/auth/abac-policies.ts:77`. **Closed-enum at the
|
|
24
|
+
* type level; runtime validation is `typeof === "string"` only**
|
|
25
|
+
* (faithful courier — mirror of `gate.evaluate`'s `gate: "pass" | "fail"`
|
|
26
|
+
* pattern). If a future kernel emits a new effect (unlikely — the
|
|
27
|
+
* NIST SP 800-162 decision algorithm is fundamentally allow/deny),
|
|
28
|
+
* the value round-trips at runtime.
|
|
29
|
+
*/
|
|
30
|
+
export type AbacPolicyEffect = "allow" | "deny";
|
|
31
|
+
/**
|
|
32
|
+
* Stable enum of resources an ABAC policy can target. Mirror of the
|
|
33
|
+
* kernel's `Resource` type alias at `src/lib/auth.ts:5-15` AND the
|
|
34
|
+
* runtime `RESOURCES` const at `src/lib/auth.ts:29-40`. The SDK's
|
|
35
|
+
* mirrored runtime array (`ABAC_POLICY_RESOURCES`) ships in the
|
|
36
|
+
* `.create()` build round (session 22) when input pre-validation
|
|
37
|
+
* needs it; for `.list()` only the type alias is used.
|
|
38
|
+
*
|
|
39
|
+
* **Closed-enum at the type level; runtime is faithful-courier**
|
|
40
|
+
* (P2 validator checks `typeof === "string"` only). A kernel-side
|
|
41
|
+
* RESOURCES addition would surface in the drift suite (spec-diff
|
|
42
|
+
* round) before consumer regressions.
|
|
43
|
+
*/
|
|
44
|
+
export type AbacPolicyResource = "systems" | "assessments" | "documents" | "attestations" | "evidence" | "users" | "api_keys" | "audit_log" | "organization" | "regulations";
|
|
45
|
+
/**
|
|
46
|
+
* Stable enum of actions an ABAC policy can gate. Mirror of the
|
|
47
|
+
* kernel's `Action` type alias at `src/lib/auth.ts:17` AND the runtime
|
|
48
|
+
* `ACTIONS` const at `src/lib/auth.ts:42-48`.
|
|
49
|
+
*/
|
|
50
|
+
export type AbacPolicyAction = "create" | "read" | "update" | "delete" | "manage";
|
|
51
|
+
/**
|
|
52
|
+
* Root for attribute paths in a condition leaf. The kernel rejects
|
|
53
|
+
* paths not rooted at `principal.<...>` or `resource.<...>` via the
|
|
54
|
+
* `SAFE_PATH_RE` regex at `src/lib/auth/abac-policies.ts:143`.
|
|
55
|
+
*/
|
|
56
|
+
export type AbacAttrRoot = "principal" | "resource";
|
|
57
|
+
/**
|
|
58
|
+
* Dotted attribute path like `principal.id` or `resource.ownerId`. The
|
|
59
|
+
* kernel's `SAFE_PATH_RE` requires `\.[A-Za-z_][A-Za-z0-9_]*` segments
|
|
60
|
+
* after the root and rejects `__proto__` / `constructor` / `prototype`
|
|
61
|
+
* via a separate `FORBIDDEN_KEYS` check. The SDK's TypeScript template-
|
|
62
|
+
* literal type only encodes the root prefix — segment-level shape is
|
|
63
|
+
* documented but not type-enforced (TS template-literal recursion
|
|
64
|
+
* costs aren't worth the small ergonomic win here).
|
|
65
|
+
*/
|
|
66
|
+
export type AbacAttrPath = `${AbacAttrRoot}.${string}`;
|
|
67
|
+
/**
|
|
68
|
+
* A primitive value an attribute can equal / be-in. Strings, numbers,
|
|
69
|
+
* and booleans only. Mirror of the kernel's `AttrValue` at
|
|
70
|
+
* `src/lib/auth/abac-policies.ts:63`. `null` / `undefined` are NOT in
|
|
71
|
+
* the union — the evaluator treats missing attributes specially (see
|
|
72
|
+
* `evaluateCondition` at `src/lib/auth/abac-policies.ts:486-578`).
|
|
73
|
+
*/
|
|
74
|
+
export type AbacAttrValue = string | number | boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Leaf condition — one of 8 operator shapes. Mirror of the kernel's
|
|
77
|
+
* `LeafCondition` at `src/lib/auth/abac-policies.ts:65-69`. The
|
|
78
|
+
* canonical validator enforces per-op allowed-key sets server-side
|
|
79
|
+
* (`src/lib/auth/abac-policies.ts:220-232`) — admin typos like
|
|
80
|
+
* `valuee:` get rejected, not silently dropped.
|
|
81
|
+
*/
|
|
82
|
+
export type AbacLeafCondition = {
|
|
83
|
+
op: "eq" | "ne";
|
|
84
|
+
attr: AbacAttrPath;
|
|
85
|
+
value: AbacAttrValue;
|
|
86
|
+
} | {
|
|
87
|
+
op: "in" | "notIn";
|
|
88
|
+
attr: AbacAttrPath;
|
|
89
|
+
values: AbacAttrValue[];
|
|
90
|
+
} | {
|
|
91
|
+
op: "exists" | "notExists";
|
|
92
|
+
attr: AbacAttrPath;
|
|
93
|
+
} | {
|
|
94
|
+
op: "attrEq" | "attrNe";
|
|
95
|
+
left: AbacAttrPath;
|
|
96
|
+
right: AbacAttrPath;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Compound condition — `and` / `or` / `not`. Mirror of the kernel's
|
|
100
|
+
* `CompoundCondition` at `src/lib/auth/abac-policies.ts:71-73`. The
|
|
101
|
+
* canonical validator enforces depth + clause budgets server-side
|
|
102
|
+
* (`MAX_DEPTH = 8`, `MAX_CLAUSES_PER_COMPOUND = 32`,
|
|
103
|
+
* `MAX_NODES_PER_EVALUATION = 1000`).
|
|
104
|
+
*/
|
|
105
|
+
export type AbacCompoundCondition = {
|
|
106
|
+
op: "and" | "or";
|
|
107
|
+
clauses: AbacCondition[];
|
|
108
|
+
} | {
|
|
109
|
+
op: "not";
|
|
110
|
+
clause: AbacCondition;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Full condition AST — discriminated union of leaf + compound.
|
|
114
|
+
* Mirror of the kernel's `AbacCondition` at
|
|
115
|
+
* `src/lib/auth/abac-policies.ts:75`.
|
|
116
|
+
*/
|
|
117
|
+
export type AbacCondition = AbacLeafCondition | AbacCompoundCondition;
|
|
118
|
+
/**
|
|
119
|
+
* An ABAC policy row as returned by `.list()` / `.retrieve()` /
|
|
120
|
+
* `.create()` / `.update()` / `.delete()`. Mirror of the kernel's
|
|
121
|
+
* `AbacPolicyRow` at `src/lib/auth/abac-policies.ts:88-98`.
|
|
122
|
+
*
|
|
123
|
+
* **Wire-shape note — Dates are ISO strings, not Date objects.** The
|
|
124
|
+
* kernel's TypeScript type declares `createdAt: Date` / `updatedAt:
|
|
125
|
+
* Date` (it's a Drizzle `timestamp` column inferred as `Date` post-
|
|
126
|
+
* fetch), but the response goes through `NextResponse.json(...)` which
|
|
127
|
+
* serializes Dates via `JSON.stringify(new Date(...))` → ISO-8601
|
|
128
|
+
* string. The SDK type reflects the wire reality (`string`), not the
|
|
129
|
+
* kernel's TypeScript intermediate.
|
|
130
|
+
*
|
|
131
|
+
* **`description`, `createdByUserId` — `string | null`** (NOT
|
|
132
|
+
* `string | undefined`). The kernel uses `null` for unset values via
|
|
133
|
+
* the `?? null` coalesce at `src/lib/auth/abac-policies.ts:706, 713`;
|
|
134
|
+
* the field is ALWAYS present on the wire, with value `null` when
|
|
135
|
+
* unset. Consumers comparing should use `policy.description !== null`
|
|
136
|
+
* (or truthy coercion, since null is falsy).
|
|
137
|
+
*
|
|
138
|
+
* **`condition` — recursive AST** mirroring the kernel grammar (8 leaf
|
|
139
|
+
* ops + 3 compound ops). Consumers can branch on `condition.op` to
|
|
140
|
+
* destructure. Server-side validation enforces depth / clause / value-
|
|
141
|
+
* list / total-node budgets; the SDK does NOT re-validate the AST
|
|
142
|
+
* after the kernel returns it (faithful courier).
|
|
143
|
+
*/
|
|
144
|
+
export interface AbacPolicy {
|
|
145
|
+
/** UUID of the policy row. */
|
|
146
|
+
id: string;
|
|
147
|
+
/** UUID of the owning org. Always equals the auth caller's `orgId`. */
|
|
148
|
+
orgId: string;
|
|
149
|
+
/** Short identifier (1-128 chars). UNIQUE per `(orgId, name)` — */
|
|
150
|
+
name: string;
|
|
151
|
+
/**
|
|
152
|
+
* Optional human-readable description (max 2000 chars on the kernel's
|
|
153
|
+
* Zod schema). `null` when unset. **NOT `undefined`** — the kernel
|
|
154
|
+
* uses `?? null` coalesce server-side, so `description` is always an
|
|
155
|
+
* own-property on the wire.
|
|
156
|
+
*/
|
|
157
|
+
description: string | null;
|
|
158
|
+
/** Resource the policy targets (closed-enum). */
|
|
159
|
+
resource: AbacPolicyResource;
|
|
160
|
+
/** Action the policy gates (closed-enum). */
|
|
161
|
+
action: AbacPolicyAction;
|
|
162
|
+
/** Effect — `allow` grants access, `deny` denies (NIST SP 800-162 §5.2). */
|
|
163
|
+
effect: AbacPolicyEffect;
|
|
164
|
+
/** Recursive condition AST evaluated against `{principal, resource}`. */
|
|
165
|
+
condition: AbacCondition;
|
|
166
|
+
/**
|
|
167
|
+
* Priority [0, 1000] used to order policies before evaluation (ASC
|
|
168
|
+
* — lower-priority policies are evaluated first, but `deny` short-
|
|
169
|
+
* circuits regardless of order). Default 100 server-side.
|
|
170
|
+
*/
|
|
171
|
+
priority: number;
|
|
172
|
+
/** When `false`, the policy is excluded from the per-request fetch. */
|
|
173
|
+
enabled: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* UUID of the user who created the policy. `null` when the policy
|
|
176
|
+
* was inserted directly via DB (e.g., migrations / fixtures), or
|
|
177
|
+
* when the creator's user row was deleted (ON DELETE SET NULL).
|
|
178
|
+
*/
|
|
179
|
+
createdByUserId: string | null;
|
|
180
|
+
/** ISO-8601 timestamp of the insert. */
|
|
181
|
+
createdAt: string;
|
|
182
|
+
/** ISO-8601 timestamp of the most recent update (or insert if never updated). */
|
|
183
|
+
updatedAt: string;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Response shape returned by `.list()`. The kernel emits
|
|
187
|
+
* `{success: true, data: {items: AbacPolicy[], count: number}}`; the
|
|
188
|
+
* transport unwraps `data`, so consumers receive `{items, count}`.
|
|
189
|
+
*
|
|
190
|
+
* **No pagination** — `count` is `items.length` (NOT a total org
|
|
191
|
+
* count beyond the materialized page). Server-side cap is
|
|
192
|
+
* `MAX_POLICIES_PER_ORG_FETCH = 200`. Orgs with >200 policies see
|
|
193
|
+
* only the LOWEST 200 by priority ASC. Documented kernel surface
|
|
194
|
+
* gap — invariant #50.
|
|
195
|
+
*/
|
|
196
|
+
export interface AbacPoliciesListResponse {
|
|
197
|
+
/** Up to 200 policies ordered by `priority` ASC, server-truncated. */
|
|
198
|
+
items: AbacPolicy[];
|
|
199
|
+
/** `items.length` — same as `items.length`; NOT a total org count. */
|
|
200
|
+
count: number;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Input shape for `.create()`. Mirror of the kernel's
|
|
204
|
+
* `createAbacPolicySchema` Zod at
|
|
205
|
+
* `src/lib/auth/abac-policies.ts:750-761`. The kernel applies
|
|
206
|
+
* **`.strict()`** — any extra fields are rejected with 422.
|
|
207
|
+
*
|
|
208
|
+
* **Required fields**: `name`, `resource`, `action`, `condition`.
|
|
209
|
+
*
|
|
210
|
+
* **Closed-spec defaults** (kernel applies if SDK omits):
|
|
211
|
+
* - `effect` defaults to `"allow"`.
|
|
212
|
+
* - `priority` defaults to `100`.
|
|
213
|
+
* - `enabled` defaults to `true`.
|
|
214
|
+
* Per invariant #52, the SDK OMITS the field from the body when the
|
|
215
|
+
* consumer omits it (so the kernel applies its default). Documenting
|
|
216
|
+
* each default prominently below.
|
|
217
|
+
*
|
|
218
|
+
* **`condition` is the recursive AST** (`AbacCondition`). The SDK
|
|
219
|
+
* does NOT pre-validate the AST grammar — the kernel's canonical
|
|
220
|
+
* validator runs server-side and rejects invalid shapes (depth >8,
|
|
221
|
+
* clauses >32 per compound, values >64 per list, total nodes >1000,
|
|
222
|
+
* unknown ops, malformed attr paths). **First SDK route with
|
|
223
|
+
* partial Zod pre-validation**: closed-spec fields are pre-validated
|
|
224
|
+
* SDK-side (UUID-style closed-enum + length bounds), but the
|
|
225
|
+
* recursive AST field defers entirely to server. Consumers see
|
|
226
|
+
* `AbacPolicyValidationError` as 422 with `details: { errors:
|
|
227
|
+
* string[] }` for AST violations; closed-spec rule violations
|
|
228
|
+
* surface as `BodyParseError` 422 with `details:
|
|
229
|
+
* Array<{path, message}>`.
|
|
230
|
+
*
|
|
231
|
+
* **Fifth SDK route to PRE-VALIDATE every Zod closed-spec rule
|
|
232
|
+
* synchronously** (after `check.run`, `gate.evaluate`,
|
|
233
|
+
* `batch.submit`, `shipGate.check`). **FIRST SDK route with PARTIAL
|
|
234
|
+
* pre-validation** — the `condition` field is an OPEN-spec rule
|
|
235
|
+
* (`z.record(z.string(), z.unknown())`) so AST grammar validation
|
|
236
|
+
* defers entirely to the server's canonical validator. Calibration:
|
|
237
|
+
* pre-validatable closed-spec rules are `name.min(1).max(128)`,
|
|
238
|
+
* `description.max(2000).optional().nullable()`,
|
|
239
|
+
* `resource`/`action`/`effect` closed-enums,
|
|
240
|
+
* `priority.int().min(0).max(1000)`, `enabled.boolean()`. The
|
|
241
|
+
* `condition` field is the only non-closed-spec rule on the schema.
|
|
242
|
+
*/
|
|
243
|
+
export interface AbacPolicyCreateInput {
|
|
244
|
+
/**
|
|
245
|
+
* Short identifier (1-128 chars). UNIQUE per `(orgId, name)` on the
|
|
246
|
+
* `abac_policies` table — duplicates trip `AbacPolicyNameConflictError`
|
|
247
|
+
* which surfaces as HTTP 409.
|
|
248
|
+
*/
|
|
249
|
+
name: string;
|
|
250
|
+
/**
|
|
251
|
+
* Optional description (max 2000 chars). Accepts `string`, `null`,
|
|
252
|
+
* or `undefined` (omitted). When omitted, the kernel persists `null`.
|
|
253
|
+
*/
|
|
254
|
+
description?: string | null;
|
|
255
|
+
/** Resource the policy targets (closed-enum). */
|
|
256
|
+
resource: AbacPolicyResource;
|
|
257
|
+
/** Action the policy gates (closed-enum). */
|
|
258
|
+
action: AbacPolicyAction;
|
|
259
|
+
/**
|
|
260
|
+
* `"allow"` grants access, `"deny"` denies. Default `"allow"` —
|
|
261
|
+
* the SDK OMITS this field when consumer omits it; kernel applies
|
|
262
|
+
* `"allow"`. Per invariant #52.
|
|
263
|
+
*/
|
|
264
|
+
effect?: AbacPolicyEffect;
|
|
265
|
+
/**
|
|
266
|
+
* Recursive condition AST evaluated against `{principal, resource}`.
|
|
267
|
+
* The SDK does NOT pre-validate the AST grammar — the kernel's
|
|
268
|
+
* canonical validator at `src/lib/auth/abac-policies.ts:161-174`
|
|
269
|
+
* handles depth / clause / value-list / total-node budgets +
|
|
270
|
+
* per-operator allowed keys. SDK-side check is only "is this an
|
|
271
|
+
* object" (rejects `null`, arrays, primitives).
|
|
272
|
+
*/
|
|
273
|
+
condition: AbacCondition;
|
|
274
|
+
/**
|
|
275
|
+
* Integer [0, 1000]. Default `100` — the SDK OMITS this field when
|
|
276
|
+
* consumer omits it; kernel applies `100`. Per invariant #52.
|
|
277
|
+
* Lower values are evaluated FIRST (asc), but the deny-wins
|
|
278
|
+
* algorithm short-circuits regardless of order.
|
|
279
|
+
*/
|
|
280
|
+
priority?: number;
|
|
281
|
+
/**
|
|
282
|
+
* Per-policy enable flag. Default `true` — the SDK OMITS this field
|
|
283
|
+
* when consumer omits it; kernel applies `true`. When `false`, the
|
|
284
|
+
* policy is excluded from the per-request fetch.
|
|
285
|
+
*/
|
|
286
|
+
enabled?: boolean;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Input shape for `.update()`. Mirror of the kernel's
|
|
290
|
+
* `updateAbacPolicySchema` Zod at
|
|
291
|
+
* `src/lib/auth/abac-policies.ts:763-795`. The kernel applies
|
|
292
|
+
* **`.strict()`** — any extra fields are rejected with 422.
|
|
293
|
+
*
|
|
294
|
+
* **EVERY field is optional** — `.update()` is a PARTIAL update
|
|
295
|
+
* (PATCH semantics). Asymmetric with `AbacPolicyCreateInput`, whose
|
|
296
|
+
* `name` / `resource` / `action` / `condition` are required: the
|
|
297
|
+
* kernel's `updateAbacPolicySchema` carries `.optional()` on all 8
|
|
298
|
+
* fields. A consumer patches only the fields they want to change.
|
|
299
|
+
*
|
|
300
|
+
* **Empty-patch rejection** — the kernel schema ends in a `.refine()`
|
|
301
|
+
* rejecting a body with NO updatable field (`"PATCH body must
|
|
302
|
+
* include at least one updatable field"`). The SDK pre-rejects an
|
|
303
|
+
* empty patch — `update(id, {})`, an all-`undefined` patch, or a
|
|
304
|
+
* patch carrying ONLY unknown keys — synchronously with a
|
|
305
|
+
* `TypeError` BEFORE any fetch is issued.
|
|
306
|
+
*
|
|
307
|
+
* **`description` accepts `null`** — passing `description: null`
|
|
308
|
+
* CLEARS an existing description. `null` is a present field for the
|
|
309
|
+
* kernel's `.refine()` (`description !== undefined` is `true` when
|
|
310
|
+
* `description` is `null`), so `update(id, { description: null })`
|
|
311
|
+
* is a valid non-empty patch. An explicit `description: undefined`
|
|
312
|
+
* is treated as omission (symmetric with `.create()`).
|
|
313
|
+
*
|
|
314
|
+
* **`condition` is the recursive AST** (`AbacCondition`) — same as
|
|
315
|
+
* `.create()`. The SDK does NOT pre-validate the AST grammar; a
|
|
316
|
+
* present `condition` is pre-validated only as "is this a non-null
|
|
317
|
+
* object", and the recursive grammar defers to the kernel's
|
|
318
|
+
* canonical validator. The fields reuse `AbacPolicyResource` /
|
|
319
|
+
* `AbacPolicyAction` / `AbacPolicyEffect` / `AbacCondition` verbatim.
|
|
320
|
+
*/
|
|
321
|
+
export interface AbacPolicyUpdateInput {
|
|
322
|
+
/**
|
|
323
|
+
* New short identifier (1-128 chars). UNIQUE per `(orgId, name)` on
|
|
324
|
+
* the `abac_policies` table — a collision trips
|
|
325
|
+
* `AbacPolicyNameConflictError` (HTTP 409).
|
|
326
|
+
*/
|
|
327
|
+
name?: string;
|
|
328
|
+
/**
|
|
329
|
+
* New description (max 2000 chars). Accepts `string`, `null`, or
|
|
330
|
+
* `undefined` (omitted). Pass `null` to CLEAR an existing
|
|
331
|
+
* description — `null` counts as a present field (a non-empty
|
|
332
|
+
* patch); `undefined` is treated as omission.
|
|
333
|
+
*/
|
|
334
|
+
description?: string | null;
|
|
335
|
+
/** New resource the policy targets (closed-enum). */
|
|
336
|
+
resource?: AbacPolicyResource;
|
|
337
|
+
/** New action the policy gates (closed-enum). */
|
|
338
|
+
action?: AbacPolicyAction;
|
|
339
|
+
/** New effect — `"allow"` grants access, `"deny"` denies (closed-enum). */
|
|
340
|
+
effect?: AbacPolicyEffect;
|
|
341
|
+
/**
|
|
342
|
+
* New recursive condition AST evaluated against `{principal,
|
|
343
|
+
* resource}`. The SDK pre-validates only that a present `condition`
|
|
344
|
+
* is a non-null object; the recursive AST grammar (depth / clause /
|
|
345
|
+
* value-list / total-node budgets, per-operator allowed keys) is
|
|
346
|
+
* validated server-side by the kernel's canonical validator.
|
|
347
|
+
*/
|
|
348
|
+
condition?: AbacCondition;
|
|
349
|
+
/** New priority — integer [0, 1000]. Lower values are evaluated FIRST. */
|
|
350
|
+
priority?: number;
|
|
351
|
+
/** New per-policy enable flag. When `false`, the policy is excluded from the per-request fetch. */
|
|
352
|
+
enabled?: boolean;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* `abacPolicies` resource — sibling to all 10 existing resource classes
|
|
356
|
+
* on the SDK. The class is the landing pad for the 5-method CRUD
|
|
357
|
+
* cluster; `.list()` is the first method to ship (session 21), with
|
|
358
|
+
* `.create()` / `.retrieve()` / `.update()` / `.delete()` arriving in
|
|
359
|
+
* session 22. Resource-class-per-kernel-resource convention,
|
|
360
|
+
* invariant #43.
|
|
361
|
+
*/
|
|
362
|
+
export declare class AbacPoliciesResource {
|
|
363
|
+
private readonly client;
|
|
364
|
+
constructor(client: AttestryClient);
|
|
365
|
+
/**
|
|
366
|
+
* List ABAC policies for the caller's org. Returns up to 200 rows
|
|
367
|
+
* ordered by `priority` ASC. No pagination; the cap is a documented
|
|
368
|
+
* kernel surface gap.
|
|
369
|
+
*
|
|
370
|
+
* **Dual-auth admin scope** — the kernel route gates on
|
|
371
|
+
* `requireSessionOrApiKey(request, { sessionRoles: ["admin"],
|
|
372
|
+
* apiKeyPermissions: [API_KEY_PERMISSIONS.ADMIN] })`. The SDK's
|
|
373
|
+
* transport always sends `x-api-key`, so the api-key path is the
|
|
374
|
+
* only one reachable from SDK consumers. An api-key without the
|
|
375
|
+
* `ADMIN` permission returns 403 (NOT 401 — see status code
|
|
376
|
+
* surface below).
|
|
377
|
+
*
|
|
378
|
+
* **Status-code surface — 401 AND 403 distinguished** (verified
|
|
379
|
+
* by reading `src/lib/middleware/auth.ts:96-110` and
|
|
380
|
+
* `src/lib/middleware/permissions.ts:35-66`):
|
|
381
|
+
* - **HTTP 401**: no `x-api-key` header, empty `x-api-key`
|
|
382
|
+
* header (`""`), invalid key (no matching row), expired key.
|
|
383
|
+
* - **HTTP 403**: valid api-key in the org whose `permissions`
|
|
384
|
+
* column does NOT include `ADMIN`. Error message: `"API key
|
|
385
|
+
* lacks required permission. Required: admin. Key has: ..."`.
|
|
386
|
+
* `auditLog.export` shares this EXACT dual-auth surface — its
|
|
387
|
+
* kernel route uses the identical `requireSessionOrApiKey(...
|
|
388
|
+
* sessionRoles:["admin"], apiKeyPermissions:[ADMIN] ...)` gate, so
|
|
389
|
+
* it too returns 401 vs 403 distinctly. (The `audit-log.ts` JSDoc's
|
|
390
|
+
* prior "HTTP 401 for both" claim was a mis-read of the kernel
|
|
391
|
+
* test's mocked `AuthError(401)` — corrected in session-22 hostile
|
|
392
|
+
* review #2.)
|
|
393
|
+
*
|
|
394
|
+
* **No pagination** — `count` is `items.length`, NOT a total org
|
|
395
|
+
* count. Server-side cap is 200 rows by priority ASC. Orgs with
|
|
396
|
+
* >200 policies are silently truncated (documented invariant #50
|
|
397
|
+
* gap; the SDK does NOT auto-paginate this method because the
|
|
398
|
+
* kernel emits no cursor — there's no next-page anchor to follow).
|
|
399
|
+
*
|
|
400
|
+
* **No `writeAuditLog` side effect** — `.list()` is quiet
|
|
401
|
+
* (asymmetric with `gate.evaluate` / `batch.submit` /
|
|
402
|
+
* `shipGate.check` which all write entries).
|
|
403
|
+
*
|
|
404
|
+
* **Symmetric prototype-pollution defense — RESPONSE side only**:
|
|
405
|
+
* the P2 validator uses the module-load `objectHasOwn` snapshot on
|
|
406
|
+
* each response field read. Input side is N/A (`.list()` has no
|
|
407
|
+
* input fields). Mirror of `audit-log.verifyChain` /
|
|
408
|
+
* `regulatoryChanges.list` pattern.
|
|
409
|
+
*
|
|
410
|
+
* Errors — **happy-path precedence ordering** is rate-limit → auth
|
|
411
|
+
* → DB lookup → successResponse. **The 500-catchall is a SEPARATE
|
|
412
|
+
* DIMENSION** — any throwable not matched by the named `instanceof`
|
|
413
|
+
* arms (only `AuthError` here) falls to 500, regardless of where
|
|
414
|
+
* in the happy-path it fired.
|
|
415
|
+
* - `AttestryAPIError` (status 429) — rate limit FIRES FIRST
|
|
416
|
+
* (auto-retried by default — invariant #18; per-IP rate-limit
|
|
417
|
+
* key `abac-policies-list:${ip}` against the
|
|
418
|
+
* `assessmentLimiter` — 30 req / 1-min sliding window).
|
|
419
|
+
* - `AttestryAPIError` (status 401) — no API key OR invalid key
|
|
420
|
+
* OR expired key. Fires AFTER rate-limit.
|
|
421
|
+
* - `AttestryAPIError` (status 403) — valid api-key in the org
|
|
422
|
+
* whose permissions do NOT include `ADMIN`. Distinct branch
|
|
423
|
+
* from 401 — pin BOTH separately per the dual-auth status-
|
|
424
|
+
* code surface.
|
|
425
|
+
* - `AttestryAPIError` (status 500) — internal kernel error
|
|
426
|
+
* (scrubbed message via `internalErrorResponse`). **The 500
|
|
427
|
+
* surface is orthogonal to the precedence list above**: ANY
|
|
428
|
+
* throwable not matched by the route's single `instanceof
|
|
429
|
+
* AuthError` arm falls to 500.
|
|
430
|
+
* - `AttestryError` ("request aborted by caller") — caller-
|
|
431
|
+
* supplied `options.signal` fired.
|
|
432
|
+
* - `AttestryError` (P2 hardening) — kernel response failed
|
|
433
|
+
* SDK-side shape validation (not an object, `items` not an
|
|
434
|
+
* array, `count` not a number).
|
|
435
|
+
* - `AttestryAPIError` (P3 hardening) — kernel response had a
|
|
436
|
+
* wrong Content-Type (transport-level guard before body
|
|
437
|
+
* parsing).
|
|
438
|
+
*
|
|
439
|
+
* **Notably ABSENT**:
|
|
440
|
+
* - **No 400** — no input → nothing to validate.
|
|
441
|
+
* - **No 404** — `.list()` returns `{items: [], count: 0}` on
|
|
442
|
+
* empty result (NOT 404). 404 is only on `.retrieve()` /
|
|
443
|
+
* `.update()` / `.delete()` (session 22).
|
|
444
|
+
*
|
|
445
|
+
* **Response-shape validation** (P2 hardening — symmetric defense
|
|
446
|
+
* on response side per the module-load `objectHasOwn` snapshot;
|
|
447
|
+
* mirror of `regulatoryChanges.list` / `incidents.list` patterns):
|
|
448
|
+
* - Rejects with `AttestryError` if the kernel response isn't
|
|
449
|
+
* a non-null, non-array object.
|
|
450
|
+
* - Rejects if `items` isn't an array.
|
|
451
|
+
* - Rejects if `count` isn't a number.
|
|
452
|
+
* - Per-row item shape NOT validated (faithful courier — P4
|
|
453
|
+
* candidate; matches incidents.list / regulatoryChanges.list).
|
|
454
|
+
*
|
|
455
|
+
* **Transport-shape validation** (P3 hardening):
|
|
456
|
+
* - Rejects with `AttestryAPIError` if the kernel responds with
|
|
457
|
+
* a non-`application/json` Content-Type.
|
|
458
|
+
*
|
|
459
|
+
* @example List all ABAC policies for the caller's org
|
|
460
|
+
* ```ts
|
|
461
|
+
* const { items, count } = await client.abacPolicies.list();
|
|
462
|
+
* console.log(`${count} policies in this org:`);
|
|
463
|
+
* for (const policy of items) {
|
|
464
|
+
* console.log(` ${policy.priority} ${policy.effect} ${policy.action} ${policy.resource}: ${policy.name}`);
|
|
465
|
+
* }
|
|
466
|
+
* ```
|
|
467
|
+
*
|
|
468
|
+
* @example Inspect a policy's condition AST
|
|
469
|
+
* ```ts
|
|
470
|
+
* const { items } = await client.abacPolicies.list();
|
|
471
|
+
* const ownerOnly = items.find((p) => p.name === "owner-only");
|
|
472
|
+
* if (ownerOnly && ownerOnly.condition.op === "attrEq") {
|
|
473
|
+
* console.log(`Compares ${ownerOnly.condition.left} === ${ownerOnly.condition.right}`);
|
|
474
|
+
* }
|
|
475
|
+
* ```
|
|
476
|
+
*/
|
|
477
|
+
list(options?: RequestOptions): Promise<AbacPoliciesListResponse>;
|
|
478
|
+
/**
|
|
479
|
+
* Create a new ABAC policy in the caller's org. Returns the
|
|
480
|
+
* inserted row on success (HTTP 201).
|
|
481
|
+
*
|
|
482
|
+
* **Dual-auth admin scope** — same as `.list()`: kernel uses
|
|
483
|
+
* `requireSessionOrApiKey(request, { sessionRoles: ["admin"],
|
|
484
|
+
* apiKeyPermissions: [API_KEY_PERMISSIONS.ADMIN] })`. The SDK's
|
|
485
|
+
* transport always sends `x-api-key`, so the api-key path is the
|
|
486
|
+
* only one reachable from SDK consumers. **HTTP 401** for no/
|
|
487
|
+
* invalid/expired key, **HTTP 403** for valid-key-without-ADMIN
|
|
488
|
+
* permission. Pin BOTH branches separately.
|
|
489
|
+
*
|
|
490
|
+
* **FIRST SDK route to return HTTP 201 on success** (NOT 200).
|
|
491
|
+
* The transport unwraps the `{success:true, data}` envelope and
|
|
492
|
+
* returns the body on any 2xx status — consumers receive the
|
|
493
|
+
* created row directly. To inspect the literal HTTP status,
|
|
494
|
+
* consumers must inspect via fetch-instrumented middleware
|
|
495
|
+
* (not exposed by the SDK today; P4 candidate).
|
|
496
|
+
*
|
|
497
|
+
* **FIRST SDK route with HTTP 409 Conflict surface**. The
|
|
498
|
+
* `(org_id, name)` unique constraint trips `AbacPolicyNameConflictError`
|
|
499
|
+
* at the DB layer; the kernel maps to 409 with error message
|
|
500
|
+
* `An ABAC policy named "<name>" already exists in this organization.`.
|
|
501
|
+
* Consumers should branch on `err.status === 409` to render a
|
|
502
|
+
* specific "name taken" UX.
|
|
503
|
+
*
|
|
504
|
+
* **Three-way 422 fan-out — distinct wire shapes per error class**:
|
|
505
|
+
* The kernel's POST handler catch block has THREE 422-mapping arms:
|
|
506
|
+
*
|
|
507
|
+
* 1. **`BodyParseError`** (most common — from `parseBody(request,
|
|
508
|
+
* schema)`) → `{ success: false, error: "Validation failed.",
|
|
509
|
+
* details: Array<{ path: string, message: string }> }`. Raised
|
|
510
|
+
* when Zod's `.strict()` schema rejects (e.g., extra field,
|
|
511
|
+
* wrong type, out-of-range value).
|
|
512
|
+
* 2. **`ZodError`** (DEFENSIVE — DEAD on happy path; `parseBody`
|
|
513
|
+
* catches Zod and converts to `BodyParseError`. The catch arm
|
|
514
|
+
* exists as defense-in-depth if some other code path throws
|
|
515
|
+
* raw `ZodError`) → `{ success: false, error: "Validation
|
|
516
|
+
* failed.", details: ZodIssue[] }` (richer than BodyParseError's
|
|
517
|
+
* mapped form — includes `code`, `expected`, `received`).
|
|
518
|
+
* 3. **`AbacPolicyValidationError`** (REACHABLE — from server-side
|
|
519
|
+
* canonical AST validation in `createAbacPolicy`) → `{ success:
|
|
520
|
+
* false, error: "ABAC policy validation failed: <messages>",
|
|
521
|
+
* details: { errors: string[] } }`. Raised when the condition
|
|
522
|
+
* AST violates depth / clause / value-list / total-node budgets,
|
|
523
|
+
* or has unknown ops / malformed attr paths.
|
|
524
|
+
*
|
|
525
|
+
* SDK surfaces all three uniformly as `AttestryAPIError(422)` —
|
|
526
|
+
* consumers inspect `err.details` to discriminate:
|
|
527
|
+
* - `Array.isArray(err.details)` → BodyParseError OR ZodError
|
|
528
|
+
* (per-field validation failure; iterate for `{path, message}`).
|
|
529
|
+
* - `err.details && Array.isArray(err.details.errors)` →
|
|
530
|
+
* AbacPolicyValidationError (AST violation; iterate `details.errors`
|
|
531
|
+
* for descriptive strings).
|
|
532
|
+
*
|
|
533
|
+
* Drift-pinned in spec-diff round (both wire shapes + the DEAD
|
|
534
|
+
* ZodError catch arm).
|
|
535
|
+
*
|
|
536
|
+
* **Fifth SDK route to PRE-VALIDATE every Zod closed-spec rule
|
|
537
|
+
* synchronously** (after `check.run`, `gate.evaluate`,
|
|
538
|
+
* `batch.submit`, `shipGate.check`). **FIRST SDK route with
|
|
539
|
+
* PARTIAL pre-validation** — the SDK pre-validates all 7 closed-
|
|
540
|
+
* spec fields synchronously (name length, description length-or-
|
|
541
|
+
* null, resource/action/effect closed-enums, priority int +
|
|
542
|
+
* bounds, enabled boolean) AND defers the recursive AST validation
|
|
543
|
+
* on `condition` to the server canonical validator. The condition
|
|
544
|
+
* field is `z.record(z.string(), z.unknown())` at the schema level
|
|
545
|
+
* (OPEN-spec, not pre-validatable without shipping the full
|
|
546
|
+
* recursive validator). Invariant #49 calibration: closed-spec
|
|
547
|
+
* rules ARE pre-validatable; recursive grammar rules are NOT
|
|
548
|
+
* (too expensive to mirror).
|
|
549
|
+
*
|
|
550
|
+
* **`writeAuditLog` side effect — every successful `.create()` call
|
|
551
|
+
* writes one audit entry** with `action: "abac_policy.create"` and
|
|
552
|
+
* `resourceType: "abac_policy"` (kernel route.ts:105-120; both
|
|
553
|
+
* strings drift-pinned). Properties:
|
|
554
|
+
* - Org-scoped, hash-chained.
|
|
555
|
+
* - **Time-blocking** but error-tolerant: kernel uses
|
|
556
|
+
* `await writeAuditLog(...)`; check response latency INCLUDES
|
|
557
|
+
* the audit-log write time.
|
|
558
|
+
* - Write FAILURE does NOT fail the request (try/catch swallows).
|
|
559
|
+
* - NOT counted against `decisionsPerMonth` quota.
|
|
560
|
+
* - **Audit log is NOT written on failed create** (Zod / canonical
|
|
561
|
+
* validation / name conflict all surface BEFORE writeAuditLog).
|
|
562
|
+
*
|
|
563
|
+
* **Kernel-side 30-second timeout** (`maxDuration = 30`). Same as
|
|
564
|
+
* `.list()` and `auditLog.export`; looser than `gate.evaluate` /
|
|
565
|
+
* `shipGate.check`'s 15s. ABAC policy creation does NO heavy
|
|
566
|
+
* computation — the 30s budget is for DB I/O headroom.
|
|
567
|
+
*
|
|
568
|
+
* **Default-applied fields**: `effect` defaults to `"allow"`,
|
|
569
|
+
* `priority` defaults to `100`, `enabled` defaults to `true`. The
|
|
570
|
+
* SDK OMITS these fields from the request body when the consumer
|
|
571
|
+
* omits them (so the kernel applies its default). Per invariant #52.
|
|
572
|
+
*
|
|
573
|
+
* Errors:
|
|
574
|
+
* - `AttestryAPIError` (429) — rate limit (auto-retried; per-IP
|
|
575
|
+
* key `abac-policies-create:${ip}` against `assessmentLimiter`).
|
|
576
|
+
* - `AttestryAPIError` (401) — no/invalid/expired api-key.
|
|
577
|
+
* - `AttestryAPIError` (403) — valid api-key without ADMIN permission.
|
|
578
|
+
* - `AttestryAPIError` (422) — Zod-schema validation OR canonical-
|
|
579
|
+
* validator AST failure. Discriminate via `err.details` shape
|
|
580
|
+
* (see "Three-way 422 fan-out" above).
|
|
581
|
+
* - `AttestryAPIError` (409) — `(org_id, name)` uniqueness
|
|
582
|
+
* conflict.
|
|
583
|
+
* - `AttestryAPIError` (500) — internal kernel error (scrubbed).
|
|
584
|
+
* - `AttestryError` ("request aborted by caller") — `options.signal`
|
|
585
|
+
* fired.
|
|
586
|
+
* - `AttestryError` (P2 hardening) — kernel response failed
|
|
587
|
+
* SDK-side shape validation. Pre-validated per-row shape:
|
|
588
|
+
* non-null object + all 13 fields present and correctly typed.
|
|
589
|
+
* - `AttestryAPIError` (P3 hardening) — wrong Content-Type on
|
|
590
|
+
* response.
|
|
591
|
+
* - `TypeError` (synchronous, no fetch issued) — SDK-side input
|
|
592
|
+
* validation: missing required field, wrong type, out-of-range
|
|
593
|
+
* value, unknown closed-enum value, malformed input.
|
|
594
|
+
*
|
|
595
|
+
* @example Create a simple "owner can edit own assessments" policy
|
|
596
|
+
* ```ts
|
|
597
|
+
* const policy = await client.abacPolicies.create({
|
|
598
|
+
* name: "owner-can-edit-own",
|
|
599
|
+
* description: "Owners can edit their own assessments.",
|
|
600
|
+
* resource: "assessments",
|
|
601
|
+
* action: "update",
|
|
602
|
+
* effect: "allow",
|
|
603
|
+
* condition: {
|
|
604
|
+
* op: "attrEq",
|
|
605
|
+
* left: "principal.id",
|
|
606
|
+
* right: "resource.ownerId",
|
|
607
|
+
* },
|
|
608
|
+
* priority: 100,
|
|
609
|
+
* enabled: true,
|
|
610
|
+
* });
|
|
611
|
+
* console.log(`Created policy ${policy.id}`);
|
|
612
|
+
* ```
|
|
613
|
+
*
|
|
614
|
+
* @example Catch name-conflict (HTTP 409)
|
|
615
|
+
* ```ts
|
|
616
|
+
* try {
|
|
617
|
+
* await client.abacPolicies.create({...});
|
|
618
|
+
* } catch (err) {
|
|
619
|
+
* if (err instanceof AttestryAPIError && err.status === 409) {
|
|
620
|
+
* // Show "name taken" UX
|
|
621
|
+
* }
|
|
622
|
+
* }
|
|
623
|
+
* ```
|
|
624
|
+
*
|
|
625
|
+
* @example Discriminate 422 wire-shape (Zod vs canonical-validator)
|
|
626
|
+
* ```ts
|
|
627
|
+
* try {
|
|
628
|
+
* await client.abacPolicies.create({...});
|
|
629
|
+
* } catch (err) {
|
|
630
|
+
* if (err instanceof AttestryAPIError && err.status === 422) {
|
|
631
|
+
* const details = err.details as unknown;
|
|
632
|
+
* if (Array.isArray(details)) {
|
|
633
|
+
* // Zod field-level failures: [{path, message}, ...]
|
|
634
|
+
* } else if (details && typeof details === "object" &&
|
|
635
|
+
* Array.isArray((details as {errors?: unknown}).errors)) {
|
|
636
|
+
* // Canonical-validator AST failures: {errors: string[]}
|
|
637
|
+
* }
|
|
638
|
+
* }
|
|
639
|
+
* }
|
|
640
|
+
* ```
|
|
641
|
+
*/
|
|
642
|
+
create(input: AbacPolicyCreateInput, options?: RequestOptions): Promise<AbacPolicy>;
|
|
643
|
+
/**
|
|
644
|
+
* Retrieve one ABAC policy by id. Returns the policy row.
|
|
645
|
+
*
|
|
646
|
+
* **FIRST `abacPolicies` method with a UUID path segment** — `id`
|
|
647
|
+
* is interpolated into the request path
|
|
648
|
+
* (`/api/v1/abac-policies/<id>`). `.list()` / `.create()` hit the
|
|
649
|
+
* collection path with no segment; `.update()` / `.delete()` share
|
|
650
|
+
* this id-path shape.
|
|
651
|
+
*
|
|
652
|
+
* **Dual-auth admin scope** — same as `.list()` / `.create()`: the
|
|
653
|
+
* kernel route gates on `requireSessionOrApiKey(request, {
|
|
654
|
+
* sessionRoles: ["admin"], apiKeyPermissions:
|
|
655
|
+
* [API_KEY_PERMISSIONS.ADMIN] })`. The SDK transport always sends
|
|
656
|
+
* `x-api-key`, so the api-key path is the only one reachable from
|
|
657
|
+
* SDK consumers. **HTTP 401** for no/invalid/expired key, **HTTP
|
|
658
|
+
* 403** for a valid key whose permissions do NOT include `ADMIN`.
|
|
659
|
+
* Pin BOTH branches separately (asymmetric with carry-forward
|
|
660
|
+
* invariant #42's ADMIN-only 401-collapse).
|
|
661
|
+
*
|
|
662
|
+
* **UUID pre-validation** — the SDK pre-validates `id` against
|
|
663
|
+
* `UUID_REGEX` synchronously (`TypeError`, NO fetch issued) before
|
|
664
|
+
* constructing the URL. The kernel's own `badId` check would
|
|
665
|
+
* return HTTP 400 "Invalid policy id." on a malformed id, but the
|
|
666
|
+
* SDK pre-empts it — that 400 is reachable only via an `as any`
|
|
667
|
+
* cast or a kernel-side id-flavor change. Mirror of `batch.get`.
|
|
668
|
+
*
|
|
669
|
+
* **No `encodeURIComponent` / URIError defense on the path
|
|
670
|
+
* segment** — a string matching `UUID_REGEX` is ASCII hex digits +
|
|
671
|
+
* hyphens, so it is URL-safe verbatim and cannot trigger a
|
|
672
|
+
* `URIError`. The validated `id` is interpolated raw. Asymmetric
|
|
673
|
+
* with `decisions.retrieve` (free-form id → `encodePathSegment`
|
|
674
|
+
* with path-traversal + URIError defenses).
|
|
675
|
+
*
|
|
676
|
+
* **404 surface** — the kernel's `getAbacPolicyById(orgId, id)`
|
|
677
|
+
* returns `null` for a missing id OR a cross-org id (the
|
|
678
|
+
* `eq(orgId)` clause silently filters policies in other orgs), and
|
|
679
|
+
* the GET handler maps that to `errorResponse("ABAC policy not
|
|
680
|
+
* found.", 404)`. **Inline literal message** — distinct from
|
|
681
|
+
* `.update()` / `.delete()`'s 404, which is raised by
|
|
682
|
+
* `AbacPolicyNotFoundError` with the id-embedded message `"ABAC
|
|
683
|
+
* policy <id> not found in this organization."`.
|
|
684
|
+
*
|
|
685
|
+
* **No `writeAuditLog` side effect** — `.retrieve()` is a quiet
|
|
686
|
+
* read (same as `.list()`; asymmetric with `.create()` /
|
|
687
|
+
* `.update()` / `.delete()`, which each write an `abac_policy.*`
|
|
688
|
+
* audit-log entry).
|
|
689
|
+
*
|
|
690
|
+
* **Kernel-side 30-second timeout** (`maxDuration = 30`). Same as
|
|
691
|
+
* `.list()` / `.create()` / `auditLog.export`; looser than
|
|
692
|
+
* `gate.evaluate` / `shipGate.check`'s 15s.
|
|
693
|
+
*
|
|
694
|
+
* Errors — **happy-path precedence ordering**: UUID format (kernel
|
|
695
|
+
* `badId` — SDK-pre-empted) → rate-limit → auth → DB lookup →
|
|
696
|
+
* successResponse. **The 500-catchall is a SEPARATE DIMENSION** —
|
|
697
|
+
* any throwable not matched by the GET handler's single
|
|
698
|
+
* `instanceof AuthError` arm falls to 500.
|
|
699
|
+
* - `AttestryAPIError` (status 429) — rate limit FIRES FIRST
|
|
700
|
+
* among the network surfaces (auto-retried by default —
|
|
701
|
+
* invariant #18; per-IP key `abac-policies-get:${ip}` against
|
|
702
|
+
* `assessmentLimiter` — 30 req / 1-min sliding window).
|
|
703
|
+
* - `AttestryAPIError` (status 401) — no/invalid/expired api-key.
|
|
704
|
+
* - `AttestryAPIError` (status 403) — valid api-key whose
|
|
705
|
+
* permissions do NOT include `ADMIN`. Distinct branch from
|
|
706
|
+
* 401 — pin BOTH separately.
|
|
707
|
+
* - `AttestryAPIError` (status 404) — policy not found OR a
|
|
708
|
+
* cross-org id (kernel collapses both to "ABAC policy not
|
|
709
|
+
* found.").
|
|
710
|
+
* - `AttestryAPIError` (status 400) — kernel `badId` rejected
|
|
711
|
+
* the id. **SDK-pre-empted** — reachable from a consumer only
|
|
712
|
+
* via an `as any` cast or a kernel-side id-flavor change.
|
|
713
|
+
* - `AttestryAPIError` (status 500) — internal kernel error
|
|
714
|
+
* (scrubbed message via `internalErrorResponse`). **Orthogonal
|
|
715
|
+
* to the precedence list** — ANY throwable not matched by the
|
|
716
|
+
* route's single `instanceof AuthError` arm falls here.
|
|
717
|
+
* - `AttestryError` ("request aborted by caller") — caller-
|
|
718
|
+
* supplied `options.signal` fired.
|
|
719
|
+
* - `AttestryError` (P2 hardening) — kernel response failed
|
|
720
|
+
* SDK-side shape validation (non-object, or any of the 13
|
|
721
|
+
* `AbacPolicy` fields missing / wrong-typed).
|
|
722
|
+
* - `AttestryAPIError` (P3 hardening) — kernel response had a
|
|
723
|
+
* wrong Content-Type (transport-level guard before body
|
|
724
|
+
* parsing).
|
|
725
|
+
* - `TypeError` (synchronous, NO fetch issued) — `id` is missing,
|
|
726
|
+
* a non-string, an empty string, or not an RFC 4122 UUID.
|
|
727
|
+
*
|
|
728
|
+
* **SDK-side validation** (synchronous `TypeError`, no fetch
|
|
729
|
+
* issued):
|
|
730
|
+
* - `id`: required; must be a non-empty string matching
|
|
731
|
+
* `UUID_REGEX` (RFC 4122 hyphenated, case-insensitive).
|
|
732
|
+
*
|
|
733
|
+
* **Response-shape validation** (P2 hardening) — the shared
|
|
734
|
+
* `validateAbacPolicy` validator checks all 13 `AbacPolicy` fields
|
|
735
|
+
* via the module-load `objectHasOwn` snapshot (symmetric
|
|
736
|
+
* prototype-pollution defense). `condition` is validated as a
|
|
737
|
+
* non-null object only — the recursive AST is faithful-courier.
|
|
738
|
+
*
|
|
739
|
+
* **Transport-shape validation** (P3 hardening) — rejects with
|
|
740
|
+
* `AttestryAPIError` if the kernel responds with a
|
|
741
|
+
* non-`application/json` Content-Type.
|
|
742
|
+
*
|
|
743
|
+
* @example Retrieve a policy by id
|
|
744
|
+
* ```ts
|
|
745
|
+
* const policy = await client.abacPolicies.retrieve(
|
|
746
|
+
* "550e8400-e29b-41d4-a716-446655440000",
|
|
747
|
+
* );
|
|
748
|
+
* console.log(`${policy.effect} ${policy.action} ${policy.resource}`);
|
|
749
|
+
* ```
|
|
750
|
+
*
|
|
751
|
+
* @example Handle not-found (HTTP 404)
|
|
752
|
+
* ```ts
|
|
753
|
+
* try {
|
|
754
|
+
* return await client.abacPolicies.retrieve(id);
|
|
755
|
+
* } catch (err) {
|
|
756
|
+
* if (err instanceof AttestryAPIError && err.status === 404) {
|
|
757
|
+
* return null; // policy doesn't exist (or belongs to another org)
|
|
758
|
+
* }
|
|
759
|
+
* throw err;
|
|
760
|
+
* }
|
|
761
|
+
* ```
|
|
762
|
+
*/
|
|
763
|
+
retrieve(id: string, options?: RequestOptions): Promise<AbacPolicy>;
|
|
764
|
+
/**
|
|
765
|
+
* Update one ABAC policy by id (PARTIAL update). Returns the
|
|
766
|
+
* **updated row** — the policy as it exists AFTER the patch is
|
|
767
|
+
* applied — on HTTP 200.
|
|
768
|
+
*
|
|
769
|
+
* **SECOND SDK method using the HTTP `PATCH` verb** (`incidents.update`
|
|
770
|
+
* is the first). The kernel route is `PATCH /api/v1/abac-policies/[id]`.
|
|
771
|
+
*
|
|
772
|
+
* **Partial update — every input field is optional.** A consumer
|
|
773
|
+
* patches only the fields they want to change; omitted fields keep
|
|
774
|
+
* their current value. The SDK builds the request body from the
|
|
775
|
+
* present-and-not-`undefined` fields only — an omitted field (or an
|
|
776
|
+
* explicit `field: undefined`) is left out of the body so the kernel
|
|
777
|
+
* leaves that column untouched.
|
|
778
|
+
*
|
|
779
|
+
* **Empty-patch pre-validation.** The kernel's `updateAbacPolicySchema`
|
|
780
|
+
* ends in a `.refine()` rejecting a body with NO updatable field
|
|
781
|
+
* (`"PATCH body must include at least one updatable field"`). The
|
|
782
|
+
* SDK pre-rejects an empty patch — `update(id, {})`, an
|
|
783
|
+
* all-`undefined` patch, or a patch carrying ONLY unknown keys —
|
|
784
|
+
* synchronously with a `TypeError` (NO fetch issued) so the consumer
|
|
785
|
+
* never burns a round-trip on a guaranteed 422.
|
|
786
|
+
*
|
|
787
|
+
* **Dual-auth admin scope** — same as `.list()` / `.create()` /
|
|
788
|
+
* `.retrieve()` / `.delete()`: `requireSessionOrApiKey(request, {
|
|
789
|
+
* sessionRoles: ["admin"], apiKeyPermissions:
|
|
790
|
+
* [API_KEY_PERMISSIONS.ADMIN] })`. **HTTP 401** for no/invalid/
|
|
791
|
+
* expired key, **HTTP 403** for a valid key whose permissions do NOT
|
|
792
|
+
* include `ADMIN`. Pin BOTH branches separately.
|
|
793
|
+
*
|
|
794
|
+
* **UUID pre-validation** — `id` is pre-validated against
|
|
795
|
+
* `UUID_REGEX` synchronously via the shared `assertValidPolicyId`
|
|
796
|
+
* helper (`TypeError`, NO fetch issued). The kernel `badId` 400
|
|
797
|
+
* ("Invalid policy id.") is SDK-pre-empted. **No `encodeURIComponent`
|
|
798
|
+
* / URIError defense** — a validated UUID is ASCII hex + hyphens and
|
|
799
|
+
* is interpolated into the path raw (mirror of `batch.get`).
|
|
800
|
+
*
|
|
801
|
+
* **Partial Zod pre-validation** — the closed-spec fields that ARE
|
|
802
|
+
* present are pre-validated synchronously (name length, description
|
|
803
|
+
* length-or-null, resource/action/effect closed-enums, priority
|
|
804
|
+
* int+bounds, enabled boolean); a present `condition` is checked
|
|
805
|
+
* only as a non-null object, deferring the recursive AST grammar to
|
|
806
|
+
* the kernel's canonical validator. Mirror of `.create()`'s partial
|
|
807
|
+
* pre-validation — but here EVERY field is optional.
|
|
808
|
+
*
|
|
809
|
+
* **`description: null` CLEARS the description.** Passing
|
|
810
|
+
* `description: null` is a valid non-empty patch — the kernel
|
|
811
|
+
* persists `null`. An explicit `description: undefined` is treated
|
|
812
|
+
* as omission (symmetric with `.create()`).
|
|
813
|
+
*
|
|
814
|
+
* **Three-way 422 fan-out — same distinct wire shapes as `.create()`:**
|
|
815
|
+
* 1. **`BodyParseError`** (Zod `.strict()` rejection via
|
|
816
|
+
* `parseBody`) → `details: Array<{ path, message }>`.
|
|
817
|
+
* 2. **`ZodError`** (DEFENSIVE — DEAD on the happy path;
|
|
818
|
+
* `parseBody` catches Zod and converts to `BodyParseError`) →
|
|
819
|
+
* `details: ZodIssue[]`.
|
|
820
|
+
* 3. **`AbacPolicyValidationError`** (server-side canonical AST
|
|
821
|
+
* validation) → `details: { errors: string[] }`.
|
|
822
|
+
* SDK surfaces all three uniformly as `AttestryAPIError(422)` —
|
|
823
|
+
* discriminate via `err.details` (see the `.create()` examples).
|
|
824
|
+
*
|
|
825
|
+
* **HTTP 409 Conflict** — patching `name` to a value already used
|
|
826
|
+
* by a sibling policy in the org trips the `(orgId, name)` unique
|
|
827
|
+
* constraint → `AbacPolicyNameConflictError` → 409.
|
|
828
|
+
*
|
|
829
|
+
* **HTTP 404** — the kernel's `updateAbacPolicy` throws
|
|
830
|
+
* `AbacPolicyNotFoundError` when the `(id, orgId)`-scoped lookup
|
|
831
|
+
* misses (a missing id OR a cross-org id). **The message is
|
|
832
|
+
* id-embedded** — `"ABAC policy <id> not found in this
|
|
833
|
+
* organization."` — same shape as `.delete()`'s 404 (distinct from
|
|
834
|
+
* `.retrieve()`'s INLINE `"ABAC policy not found."`).
|
|
835
|
+
*
|
|
836
|
+
* **6 named-error catch arms — the LARGEST on the SDK**, in order:
|
|
837
|
+
* `AuthError`, `BodyParseError`, `ZodError`,
|
|
838
|
+
* `AbacPolicyValidationError`, `AbacPolicyNameConflictError`,
|
|
839
|
+
* `AbacPolicyNotFoundError`. Everything else falls to the 500
|
|
840
|
+
* `internalErrorResponse` catchall.
|
|
841
|
+
*
|
|
842
|
+
* **`writeAuditLog` side effect** — every successful `.update()`
|
|
843
|
+
* writes one audit-log entry with `action: "abac_policy.update"`
|
|
844
|
+
* and `resourceType: "abac_policy"`; the entry's `details` records
|
|
845
|
+
* the changed field names plus a structured `before`/`after` diff.
|
|
846
|
+
* The write is org-scoped + hash-chained, `await`-ed (so `.update()`
|
|
847
|
+
* latency includes the audit write) but error-tolerant (a write
|
|
848
|
+
* failure does NOT fail the request) and is NOT counted against any
|
|
849
|
+
* quota. The audit log is NOT written on a failed update — 404 /
|
|
850
|
+
* 409 / 422 all surface BEFORE the `writeAuditLog` call.
|
|
851
|
+
*
|
|
852
|
+
* **Kernel-side 30-second timeout** (`maxDuration = 30`). Same as
|
|
853
|
+
* `.list()` / `.create()` / `.retrieve()` / `.delete()`.
|
|
854
|
+
*
|
|
855
|
+
* Errors — **happy-path precedence ordering**: UUID format (kernel
|
|
856
|
+
* `badId` — SDK-pre-empted) → rate-limit → auth → body parse → DB
|
|
857
|
+
* lookup/update → audit write → successResponse.
|
|
858
|
+
* - `AttestryAPIError` (429) — rate limit FIRES FIRST among the
|
|
859
|
+
* network surfaces (auto-retried; per-IP key
|
|
860
|
+
* `abac-policies-patch:${ip}` against `assessmentLimiter`).
|
|
861
|
+
* - `AttestryAPIError` (401) — no/invalid/expired api-key.
|
|
862
|
+
* - `AttestryAPIError` (403) — valid api-key without `ADMIN`.
|
|
863
|
+
* Distinct branch from 401 — pin BOTH separately.
|
|
864
|
+
* - `AttestryAPIError` (422) — Zod-schema validation OR canonical-
|
|
865
|
+
* validator AST failure. Discriminate via `err.details` shape.
|
|
866
|
+
* - `AttestryAPIError` (409) — `(orgId, name)` uniqueness conflict.
|
|
867
|
+
* - `AttestryAPIError` (404) — policy not found OR a cross-org id
|
|
868
|
+
* (`AbacPolicyNotFoundError`; id-embedded message).
|
|
869
|
+
* - `AttestryAPIError` (400) — kernel `badId` rejected the id.
|
|
870
|
+
* **SDK-pre-empted**.
|
|
871
|
+
* - `AttestryAPIError` (500) — internal kernel error (scrubbed).
|
|
872
|
+
* - `AttestryError` ("request aborted by caller") — `options.signal`
|
|
873
|
+
* fired.
|
|
874
|
+
* - `AttestryError` (P2 hardening) — the updated row failed
|
|
875
|
+
* SDK-side shape validation (non-object, or any of the 13
|
|
876
|
+
* `AbacPolicy` fields missing / wrong-typed).
|
|
877
|
+
* - `AttestryAPIError` (P3 hardening) — wrong Content-Type on the
|
|
878
|
+
* response.
|
|
879
|
+
* - `TypeError` (synchronous, NO fetch issued) — invalid `id`;
|
|
880
|
+
* `input` not a non-null object; a present field is the wrong
|
|
881
|
+
* type / out of range / an unknown closed-enum value; OR the
|
|
882
|
+
* patch is empty (no updatable field).
|
|
883
|
+
*
|
|
884
|
+
* @example Patch a single field (the rest of the policy is unchanged)
|
|
885
|
+
* ```ts
|
|
886
|
+
* const updated = await client.abacPolicies.update(
|
|
887
|
+
* "550e8400-e29b-41d4-a716-446655440000",
|
|
888
|
+
* { enabled: false },
|
|
889
|
+
* );
|
|
890
|
+
* console.log(`Policy "${updated.name}" is now ${updated.enabled ? "on" : "off"}`);
|
|
891
|
+
* ```
|
|
892
|
+
*
|
|
893
|
+
* @example Clear a description (pass null) and re-prioritize
|
|
894
|
+
* ```ts
|
|
895
|
+
* await client.abacPolicies.update(id, { description: null, priority: 10 });
|
|
896
|
+
* ```
|
|
897
|
+
*
|
|
898
|
+
* @example Catch a name-conflict (HTTP 409)
|
|
899
|
+
* ```ts
|
|
900
|
+
* try {
|
|
901
|
+
* await client.abacPolicies.update(id, { name: "taken-name" });
|
|
902
|
+
* } catch (err) {
|
|
903
|
+
* if (err instanceof AttestryAPIError && err.status === 409) {
|
|
904
|
+
* // another policy in the org already uses that name
|
|
905
|
+
* }
|
|
906
|
+
* }
|
|
907
|
+
* ```
|
|
908
|
+
*/
|
|
909
|
+
update(id: string, input: AbacPolicyUpdateInput, options?: RequestOptions): Promise<AbacPolicy>;
|
|
910
|
+
/**
|
|
911
|
+
* Delete one ABAC policy by id. Returns the **deleted row** — the
|
|
912
|
+
* policy as it existed immediately before deletion — on HTTP 200.
|
|
913
|
+
*
|
|
914
|
+
* **Returns the deleted row, NOT `void`.** The kernel's DELETE
|
|
915
|
+
* handler emits `successResponse(row, 200)` carrying the
|
|
916
|
+
* just-deleted `AbacPolicy`, so a caller can log / audit / render
|
|
917
|
+
* an undo affordance with the full prior state. Consumers MUST NOT
|
|
918
|
+
* expect `Promise<void>` or a `{ deleted: true }` envelope — the
|
|
919
|
+
* resolved value is a complete `AbacPolicy`.
|
|
920
|
+
*
|
|
921
|
+
* **FIRST SDK method using the HTTP `DELETE` verb.** Every prior
|
|
922
|
+
* SDK route is GET / POST / PATCH. The transport's
|
|
923
|
+
* `InternalRequestArgs.method` union already includes `"DELETE"`,
|
|
924
|
+
* so no new transport primitive is needed.
|
|
925
|
+
*
|
|
926
|
+
* **Dual-auth admin scope** — same as `.list()` / `.create()` /
|
|
927
|
+
* `.retrieve()`: `requireSessionOrApiKey(request, { sessionRoles:
|
|
928
|
+
* ["admin"], apiKeyPermissions: [API_KEY_PERMISSIONS.ADMIN] })`.
|
|
929
|
+
* **HTTP 401** for no/invalid/expired key, **HTTP 403** for a valid
|
|
930
|
+
* key whose permissions do NOT include `ADMIN`. Pin BOTH branches.
|
|
931
|
+
*
|
|
932
|
+
* **UUID pre-validation** — `id` is pre-validated against
|
|
933
|
+
* `UUID_REGEX` synchronously (`TypeError`, NO fetch issued) via the
|
|
934
|
+
* shared `assertValidPolicyId` helper. The kernel `badId` 400
|
|
935
|
+
* ("Invalid policy id.") is SDK-pre-empted. **No `encodeURIComponent`
|
|
936
|
+
* / URIError defense** — a validated UUID is ASCII hex + hyphens and
|
|
937
|
+
* is interpolated into the path raw (see `UUID_REGEX`; mirror of
|
|
938
|
+
* `batch.get`).
|
|
939
|
+
*
|
|
940
|
+
* **404 surface** — the kernel's `deleteAbacPolicy(orgId, id)`
|
|
941
|
+
* throws `AbacPolicyNotFoundError` when the `(id, orgId)`-scoped
|
|
942
|
+
* delete matches zero rows (a missing id OR a cross-org id — the
|
|
943
|
+
* `eq(orgId)` clause scopes the delete). The DELETE handler maps it
|
|
944
|
+
* to `errorResponse(error.message, 404)`. **The message is
|
|
945
|
+
* id-embedded** — `"ABAC policy <id> not found in this
|
|
946
|
+
* organization."` — distinct from `.retrieve()`'s INLINE
|
|
947
|
+
* `"ABAC policy not found."`. (`.retrieve()` uses an inline string;
|
|
948
|
+
* `.update()` / `.delete()` raise `AbacPolicyNotFoundError`.)
|
|
949
|
+
*
|
|
950
|
+
* **`writeAuditLog` side effect — every successful `.delete()` call
|
|
951
|
+
* writes one audit-log entry** with `action: "abac_policy.delete"`
|
|
952
|
+
* and `resourceType: "abac_policy"`. The entry's `details` records
|
|
953
|
+
* the deleted policy's `name` / `resource` / `action` / `effect`
|
|
954
|
+
* for forensics. The write is org-scoped + hash-chained; it is
|
|
955
|
+
* `await`-ed (so `.delete()` latency includes the audit write) but
|
|
956
|
+
* error-tolerant (a write failure does NOT fail the request); it is
|
|
957
|
+
* NOT counted against any quota. The audit log is NOT written on a
|
|
958
|
+
* failed delete — a 404 surfaces BEFORE the `writeAuditLog` call.
|
|
959
|
+
*
|
|
960
|
+
* **Kernel-side 30-second timeout** (`maxDuration = 30`). Same as
|
|
961
|
+
* `.list()` / `.create()` / `.retrieve()`.
|
|
962
|
+
*
|
|
963
|
+
* Errors — **happy-path precedence ordering**: UUID format (kernel
|
|
964
|
+
* `badId` — SDK-pre-empted) → rate-limit → auth → DB delete → audit
|
|
965
|
+
* write → successResponse. **The 500-catchall is a SEPARATE
|
|
966
|
+
* DIMENSION** — any throwable not matched by the DELETE handler's 2
|
|
967
|
+
* `instanceof` arms (`AuthError`, `AbacPolicyNotFoundError`) falls
|
|
968
|
+
* to 500.
|
|
969
|
+
* - `AttestryAPIError` (status 429) — rate limit FIRES FIRST
|
|
970
|
+
* among the network surfaces (auto-retried by default —
|
|
971
|
+
* invariant #18; per-IP key `abac-policies-delete:${ip}`
|
|
972
|
+
* against `assessmentLimiter` — 30 req / 1-min sliding window).
|
|
973
|
+
* - `AttestryAPIError` (status 401) — no/invalid/expired api-key.
|
|
974
|
+
* - `AttestryAPIError` (status 403) — valid api-key without
|
|
975
|
+
* `ADMIN`. Distinct branch from 401 — pin BOTH separately.
|
|
976
|
+
* - `AttestryAPIError` (status 404) — policy not found OR a
|
|
977
|
+
* cross-org id (`AbacPolicyNotFoundError`; id-embedded message).
|
|
978
|
+
* - `AttestryAPIError` (status 400) — kernel `badId` rejected the
|
|
979
|
+
* id. **SDK-pre-empted** — reachable from a consumer only via
|
|
980
|
+
* an `as any` cast or a kernel-side id-flavor change.
|
|
981
|
+
* - `AttestryAPIError` (status 500) — internal kernel error
|
|
982
|
+
* (scrubbed message via `internalErrorResponse`). **Orthogonal
|
|
983
|
+
* to the precedence list** — ANY throwable not matched by the
|
|
984
|
+
* route's 2 `instanceof` arms falls here.
|
|
985
|
+
* - `AttestryError` ("request aborted by caller") — caller-
|
|
986
|
+
* supplied `options.signal` fired.
|
|
987
|
+
* - `AttestryError` (P2 hardening) — the deleted row failed
|
|
988
|
+
* SDK-side shape validation (non-object, or any of the 13
|
|
989
|
+
* `AbacPolicy` fields missing / wrong-typed).
|
|
990
|
+
* - `AttestryAPIError` (P3 hardening) — kernel response had a
|
|
991
|
+
* wrong Content-Type (transport-level guard before body
|
|
992
|
+
* parsing).
|
|
993
|
+
* - `TypeError` (synchronous, NO fetch issued) — `id` is missing,
|
|
994
|
+
* a non-string, an empty string, or not an RFC 4122 UUID.
|
|
995
|
+
*
|
|
996
|
+
* **SDK-side validation** (synchronous `TypeError`, no fetch
|
|
997
|
+
* issued):
|
|
998
|
+
* - `id`: required; must be a non-empty string matching
|
|
999
|
+
* `UUID_REGEX` (RFC 4122 hyphenated, case-insensitive).
|
|
1000
|
+
*
|
|
1001
|
+
* **Response-shape validation** (P2 hardening) — the shared
|
|
1002
|
+
* `validateAbacPolicy` validator checks all 13 `AbacPolicy` fields
|
|
1003
|
+
* of the deleted row via the module-load `objectHasOwn` snapshot.
|
|
1004
|
+
* `condition` is validated as a non-null object only — the
|
|
1005
|
+
* recursive AST is faithful-courier.
|
|
1006
|
+
*
|
|
1007
|
+
* **Transport-shape validation** (P3 hardening) — rejects with
|
|
1008
|
+
* `AttestryAPIError` if the kernel responds with a
|
|
1009
|
+
* non-`application/json` Content-Type.
|
|
1010
|
+
*
|
|
1011
|
+
* @example Delete a policy and log the prior state
|
|
1012
|
+
* ```ts
|
|
1013
|
+
* const deleted = await client.abacPolicies.delete(
|
|
1014
|
+
* "550e8400-e29b-41d4-a716-446655440000",
|
|
1015
|
+
* );
|
|
1016
|
+
* console.log(`Deleted "${deleted.name}" (${deleted.effect} ${deleted.action})`);
|
|
1017
|
+
* ```
|
|
1018
|
+
*
|
|
1019
|
+
* @example Treat a not-found delete as idempotent success
|
|
1020
|
+
* ```ts
|
|
1021
|
+
* try {
|
|
1022
|
+
* await client.abacPolicies.delete(id);
|
|
1023
|
+
* } catch (err) {
|
|
1024
|
+
* if (err instanceof AttestryAPIError && err.status === 404) {
|
|
1025
|
+
* // already gone — fine for an idempotent caller
|
|
1026
|
+
* } else {
|
|
1027
|
+
* throw err;
|
|
1028
|
+
* }
|
|
1029
|
+
* }
|
|
1030
|
+
* ```
|
|
1031
|
+
*/
|
|
1032
|
+
delete(id: string, options?: RequestOptions): Promise<AbacPolicy>;
|
|
1033
|
+
}
|
|
1034
|
+
//# sourceMappingURL=abac-policies.d.ts.map
|