@atlasent/sdk 2.10.0 → 2.12.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/dist/behavior.cjs +2 -1
- package/dist/behavior.cjs.map +1 -1
- package/dist/behavior.d.cts +32 -5
- package/dist/behavior.d.ts +32 -5
- package/dist/behavior.js +2 -1
- package/dist/behavior.js.map +1 -1
- package/dist/hono.cjs +758 -23
- package/dist/hono.cjs.map +1 -1
- package/dist/hono.d.cts +2 -2
- package/dist/hono.d.ts +2 -2
- package/dist/hono.js +757 -23
- package/dist/hono.js.map +1 -1
- package/dist/index.cjs +4670 -1998
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2342 -565
- package/dist/index.d.ts +2342 -565
- package/dist/index.js +4632 -2027
- package/dist/index.js.map +1 -1
- package/dist/{protect-C0t0fP1y.d.cts → protect-B6w-WQMB.d.cts} +514 -281
- package/dist/{protect-C0t0fP1y.d.ts → protect-B6w-WQMB.d.ts} +514 -281
- package/package.json +1 -1
package/dist/hono.js
CHANGED
|
@@ -47,7 +47,8 @@ var KNOWN_PERMIT_OUTCOMES = /* @__PURE__ */ new Set([
|
|
|
47
47
|
"permit_consumed",
|
|
48
48
|
"permit_expired",
|
|
49
49
|
"permit_revoked",
|
|
50
|
-
"permit_not_found"
|
|
50
|
+
"permit_not_found",
|
|
51
|
+
"permit_signing_key_revoked"
|
|
51
52
|
]);
|
|
52
53
|
function normalizePermitOutcome(raw) {
|
|
53
54
|
if (raw !== void 0 && KNOWN_PERMIT_OUTCOMES.has(raw)) {
|
|
@@ -108,8 +109,198 @@ var AtlaSentDeniedError = class extends AtlaSentError {
|
|
|
108
109
|
get isNotFound() {
|
|
109
110
|
return this.outcome === "permit_not_found";
|
|
110
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* `true` when the permit's signing key KID appears in the
|
|
114
|
+
* trust-root revocation list (ADR-005 D3 R2/R3 key rotation).
|
|
115
|
+
*/
|
|
116
|
+
get isSigningKeyRevoked() {
|
|
117
|
+
return this.outcome === "permit_signing_key_revoked";
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
var BundleVerificationError = class extends AtlaSentError {
|
|
121
|
+
name = "BundleVerificationError";
|
|
122
|
+
reason;
|
|
123
|
+
snapshotValidUntil;
|
|
124
|
+
snapshotFetchedAt;
|
|
125
|
+
snapshotSource;
|
|
126
|
+
kid;
|
|
127
|
+
constructor(init) {
|
|
128
|
+
super(`AtlaSent audit bundle verification failed: ${init.reason}`);
|
|
129
|
+
this.reason = init.reason;
|
|
130
|
+
this.snapshotValidUntil = init.snapshotValidUntil;
|
|
131
|
+
this.snapshotFetchedAt = init.snapshotFetchedAt;
|
|
132
|
+
this.snapshotSource = init.snapshotSource;
|
|
133
|
+
this.kid = init.kid;
|
|
134
|
+
}
|
|
111
135
|
};
|
|
112
136
|
|
|
137
|
+
// src/trustRoot.ts
|
|
138
|
+
import { readFileSync } from "fs";
|
|
139
|
+
import { fileURLToPath } from "url";
|
|
140
|
+
import { resolve, dirname } from "path";
|
|
141
|
+
var REFRESH_INTERVAL_MS_DEFAULT = 4 * 60 * 60 * 1e3;
|
|
142
|
+
var REFRESH_INTERVAL_MS_FLOOR = 5 * 60 * 1e3;
|
|
143
|
+
var KEYS_BASE_URL = "https://keys.atlasent.io/.well-known";
|
|
144
|
+
var _halfLifeWarningEmitted = false;
|
|
145
|
+
var _expiredWarningEmitted = false;
|
|
146
|
+
var TrustRootManager = class {
|
|
147
|
+
_snapshot;
|
|
148
|
+
_refreshTimer = null;
|
|
149
|
+
_opts;
|
|
150
|
+
constructor(initialSnapshot, opts = {}) {
|
|
151
|
+
this._snapshot = initialSnapshot;
|
|
152
|
+
const intervalMs = Math.max(
|
|
153
|
+
opts.refreshIntervalMs ?? REFRESH_INTERVAL_MS_DEFAULT,
|
|
154
|
+
REFRESH_INTERVAL_MS_FLOOR
|
|
155
|
+
);
|
|
156
|
+
this._opts = {
|
|
157
|
+
refreshBaseUrl: opts.refreshBaseUrl ?? KEYS_BASE_URL,
|
|
158
|
+
refreshIntervalMs: intervalMs,
|
|
159
|
+
disableRefresh: opts.disableRefresh ?? false,
|
|
160
|
+
fetch: opts.fetch ?? (typeof globalThis !== "undefined" && globalThis.fetch ? globalThis.fetch.bind(globalThis) : ((_url) => Promise.reject(new Error("fetch not available"))))
|
|
161
|
+
};
|
|
162
|
+
if (!this._opts.disableRefresh) {
|
|
163
|
+
this._scheduleRefresh();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
getSnapshot() {
|
|
167
|
+
return this._snapshot;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Check whether the snapshot is expired, emit one-time warnings at
|
|
171
|
+
* half-life and expiry. Returns "ok" | "half_life" | "expired".
|
|
172
|
+
*
|
|
173
|
+
* Emits console.warn once per process at half-life (ADR-005 D3).
|
|
174
|
+
* Emits console.warn once per process on expiry.
|
|
175
|
+
*/
|
|
176
|
+
checkExpiry() {
|
|
177
|
+
const snap = this._snapshot;
|
|
178
|
+
const now = Date.now();
|
|
179
|
+
const issuedAt = new Date(snap.issued_at).getTime();
|
|
180
|
+
const validUntil = new Date(snap.valid_until).getTime();
|
|
181
|
+
if (now > validUntil) {
|
|
182
|
+
if (!_expiredWarningEmitted) {
|
|
183
|
+
_expiredWarningEmitted = true;
|
|
184
|
+
const daysAgo = Math.floor((now - validUntil) / (24 * 60 * 60 * 1e3));
|
|
185
|
+
console.warn(
|
|
186
|
+
`[atlasent] Trust snapshot expired ${daysAgo} day(s) ago (valid_until: ${snap.valid_until}). Update to a newer SDK build or enable allowExpiredSnapshot.`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
return "expired";
|
|
190
|
+
}
|
|
191
|
+
const window = validUntil - issuedAt;
|
|
192
|
+
const halfLife = issuedAt + window / 2;
|
|
193
|
+
if (now > halfLife) {
|
|
194
|
+
if (!_halfLifeWarningEmitted) {
|
|
195
|
+
_halfLifeWarningEmitted = true;
|
|
196
|
+
const daysLeft = Math.floor((validUntil - now) / (24 * 60 * 60 * 1e3));
|
|
197
|
+
console.warn(
|
|
198
|
+
`[atlasent] Trust snapshot at half-life: expires in ${daysLeft} day(s) (valid_until: ${snap.valid_until}). Plan an SDK update.`
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
return "half_life";
|
|
202
|
+
}
|
|
203
|
+
return "ok";
|
|
204
|
+
}
|
|
205
|
+
/** Look up a key entry by kid. Returns undefined if not found. */
|
|
206
|
+
lookupKey(kid) {
|
|
207
|
+
return this._snapshot.keys.find((k) => k.kid === kid);
|
|
208
|
+
}
|
|
209
|
+
/** Returns true if the kid appears in revoked_keys. */
|
|
210
|
+
isRevoked(kid) {
|
|
211
|
+
return this._snapshot.revoked_keys.some((r) => r.kid === kid);
|
|
212
|
+
}
|
|
213
|
+
/** Replace the snapshot (e.g. after a successful refresh). */
|
|
214
|
+
replaceSnapshot(next) {
|
|
215
|
+
this._snapshot = next;
|
|
216
|
+
}
|
|
217
|
+
stopRefresh() {
|
|
218
|
+
if (this._refreshTimer !== null) {
|
|
219
|
+
clearInterval(this._refreshTimer);
|
|
220
|
+
this._refreshTimer = null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
_scheduleRefresh() {
|
|
224
|
+
this._refreshTimer = setInterval(() => {
|
|
225
|
+
void this._doRefresh();
|
|
226
|
+
}, this._opts.refreshIntervalMs);
|
|
227
|
+
if (this._refreshTimer && typeof this._refreshTimer === "object" && "unref" in this._refreshTimer) {
|
|
228
|
+
this._refreshTimer.unref();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
async _doRefresh() {
|
|
232
|
+
try {
|
|
233
|
+
const base = this._opts.refreshBaseUrl.replace(/\/$/, "");
|
|
234
|
+
const [keysRes, revocRes] = await Promise.all([
|
|
235
|
+
this._opts.fetch(`${base}/atlasent-verifier-keys.json`),
|
|
236
|
+
this._opts.fetch(`${base}/atlasent-revocations.json`)
|
|
237
|
+
]);
|
|
238
|
+
const indexRes = await this._opts.fetch(`${base}/atlasent-trust-root.json`);
|
|
239
|
+
if (!keysRes.ok || !revocRes.ok || !indexRes.ok) return;
|
|
240
|
+
const [keys, revoc, index] = await Promise.all([
|
|
241
|
+
keysRes.json(),
|
|
242
|
+
revocRes.json(),
|
|
243
|
+
indexRes.json()
|
|
244
|
+
]);
|
|
245
|
+
if (!index.valid_until || !Array.isArray(keys.keys)) return;
|
|
246
|
+
this._snapshot = {
|
|
247
|
+
valid_until: index.valid_until,
|
|
248
|
+
issued_at: index.issued_at ?? this._snapshot.issued_at,
|
|
249
|
+
keys: keys.keys,
|
|
250
|
+
revoked_keys: revoc.revoked_keys ?? [],
|
|
251
|
+
revoked_identities: revoc.revoked_identities ?? []
|
|
252
|
+
};
|
|
253
|
+
} catch {
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
function _loadVendorSnapshot() {
|
|
258
|
+
try {
|
|
259
|
+
let packageRoot;
|
|
260
|
+
try {
|
|
261
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
262
|
+
packageRoot = resolve(dirname(thisFile), "..", "..");
|
|
263
|
+
} catch {
|
|
264
|
+
packageRoot = resolve(__dirname, "..", "..");
|
|
265
|
+
}
|
|
266
|
+
const vendorDir = resolve(packageRoot, "vendor", "trust-root");
|
|
267
|
+
const index = JSON.parse(
|
|
268
|
+
readFileSync(resolve(vendorDir, "atlasent-trust-root.json"), "utf8")
|
|
269
|
+
);
|
|
270
|
+
const verifierKeys = JSON.parse(
|
|
271
|
+
readFileSync(resolve(vendorDir, "atlasent-verifier-keys.json"), "utf8")
|
|
272
|
+
);
|
|
273
|
+
const revocations = JSON.parse(
|
|
274
|
+
readFileSync(resolve(vendorDir, "atlasent-revocations.json"), "utf8")
|
|
275
|
+
);
|
|
276
|
+
return {
|
|
277
|
+
valid_until: index.valid_until,
|
|
278
|
+
issued_at: index.issued_at,
|
|
279
|
+
keys: verifierKeys.keys ?? [],
|
|
280
|
+
revoked_keys: revocations.revoked_keys ?? [],
|
|
281
|
+
revoked_identities: revocations.revoked_identities ?? []
|
|
282
|
+
};
|
|
283
|
+
} catch {
|
|
284
|
+
return {
|
|
285
|
+
valid_until: "2099-01-01T00:00:00Z",
|
|
286
|
+
issued_at: "2026-05-26T00:00:00Z",
|
|
287
|
+
keys: [],
|
|
288
|
+
revoked_keys: [],
|
|
289
|
+
revoked_identities: []
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
var _globalManager = null;
|
|
294
|
+
function getGlobalTrustRootManager(opts) {
|
|
295
|
+
if (!_globalManager) {
|
|
296
|
+
_globalManager = new TrustRootManager(
|
|
297
|
+
_loadVendorSnapshot(),
|
|
298
|
+
opts ?? { disableRefresh: false }
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
return _globalManager;
|
|
302
|
+
}
|
|
303
|
+
|
|
113
304
|
// src/types.ts
|
|
114
305
|
var PRODUCTION_DEPLOY_ACTION = "production.deploy";
|
|
115
306
|
var DEPLOY_GATE_CODES = Object.freeze({
|
|
@@ -135,7 +326,13 @@ function normalizeEvaluateRequest(input) {
|
|
|
135
326
|
actor_id: legacy.agent
|
|
136
327
|
};
|
|
137
328
|
if (legacy.context !== void 0) normalized.context = legacy.context;
|
|
138
|
-
|
|
329
|
+
const l = legacy;
|
|
330
|
+
if (l.explain !== void 0) normalized.explain = l.explain;
|
|
331
|
+
if (l.environment !== void 0) normalized.environment = l.environment;
|
|
332
|
+
if (l.resource !== void 0) normalized.resource = l.resource;
|
|
333
|
+
if (l.current_state !== void 0) normalized.current_state = l.current_state;
|
|
334
|
+
if (l.proposed_state !== void 0) normalized.proposed_state = l.proposed_state;
|
|
335
|
+
if (l.execution_binding !== void 0) normalized.execution_binding = l.execution_binding;
|
|
139
336
|
return normalized;
|
|
140
337
|
}
|
|
141
338
|
return input;
|
|
@@ -143,9 +340,9 @@ function normalizeEvaluateRequest(input) {
|
|
|
143
340
|
|
|
144
341
|
// src/retry.ts
|
|
145
342
|
var DEFAULT_RETRY_POLICY = {
|
|
146
|
-
maxAttempts:
|
|
147
|
-
baseDelayMs:
|
|
148
|
-
maxDelayMs:
|
|
343
|
+
maxAttempts: 3,
|
|
344
|
+
baseDelayMs: 250,
|
|
345
|
+
maxDelayMs: 1e4
|
|
149
346
|
};
|
|
150
347
|
var RETRYABLE_CODES = /* @__PURE__ */ new Set([
|
|
151
348
|
"network",
|
|
@@ -194,10 +391,371 @@ function clampUnit(n) {
|
|
|
194
391
|
return n;
|
|
195
392
|
}
|
|
196
393
|
|
|
394
|
+
// src/scim.ts
|
|
395
|
+
var SCIM_USER_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:User";
|
|
396
|
+
var SCIM_GROUP_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:Group";
|
|
397
|
+
function scimUsersPath(orgId) {
|
|
398
|
+
return `/scim/v2/${encodeURIComponent(orgId)}/Users`;
|
|
399
|
+
}
|
|
400
|
+
function scimGroupsPath(orgId) {
|
|
401
|
+
return `/scim/v2/${encodeURIComponent(orgId)}/Groups`;
|
|
402
|
+
}
|
|
403
|
+
function buildScimQuery(filter, startIndex, count) {
|
|
404
|
+
const params = new URLSearchParams();
|
|
405
|
+
if (filter !== void 0) params.set("filter", filter);
|
|
406
|
+
if (startIndex !== void 0) params.set("startIndex", String(startIndex));
|
|
407
|
+
if (count !== void 0) params.set("count", String(count));
|
|
408
|
+
return params.size > 0 ? params : void 0;
|
|
409
|
+
}
|
|
410
|
+
function makeScimClient(postFn, getFn, putFn, deleteFn) {
|
|
411
|
+
const users = {
|
|
412
|
+
async list(params) {
|
|
413
|
+
const qs = buildScimQuery(
|
|
414
|
+
params.filter,
|
|
415
|
+
params.startIndex,
|
|
416
|
+
params.count
|
|
417
|
+
);
|
|
418
|
+
const { body } = await getFn(
|
|
419
|
+
scimUsersPath(params.orgId),
|
|
420
|
+
qs
|
|
421
|
+
);
|
|
422
|
+
return body;
|
|
423
|
+
},
|
|
424
|
+
async create(orgId, user) {
|
|
425
|
+
const payload = user.schemas ? user : { ...user, schemas: [SCIM_USER_SCHEMA] };
|
|
426
|
+
const { body } = await postFn(scimUsersPath(orgId), payload);
|
|
427
|
+
return body;
|
|
428
|
+
},
|
|
429
|
+
async update(orgId, id, user) {
|
|
430
|
+
const payload = user.schemas ? user : { ...user, schemas: [SCIM_USER_SCHEMA] };
|
|
431
|
+
const { body } = await putFn(
|
|
432
|
+
`${scimUsersPath(orgId)}/${encodeURIComponent(id)}`,
|
|
433
|
+
payload
|
|
434
|
+
);
|
|
435
|
+
return body;
|
|
436
|
+
},
|
|
437
|
+
async delete(orgId, id) {
|
|
438
|
+
return deleteFn(
|
|
439
|
+
`${scimUsersPath(orgId)}/${encodeURIComponent(id)}`
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
const groups = {
|
|
444
|
+
async list(params) {
|
|
445
|
+
const qs = buildScimQuery(
|
|
446
|
+
params.filter,
|
|
447
|
+
params.startIndex,
|
|
448
|
+
params.count
|
|
449
|
+
);
|
|
450
|
+
const { body } = await getFn(
|
|
451
|
+
scimGroupsPath(params.orgId),
|
|
452
|
+
qs
|
|
453
|
+
);
|
|
454
|
+
return body;
|
|
455
|
+
},
|
|
456
|
+
async create(orgId, group) {
|
|
457
|
+
const payload = group["schemas"] ? group : { ...group, schemas: [SCIM_GROUP_SCHEMA] };
|
|
458
|
+
const { body } = await postFn(
|
|
459
|
+
scimGroupsPath(orgId),
|
|
460
|
+
payload
|
|
461
|
+
);
|
|
462
|
+
return body;
|
|
463
|
+
},
|
|
464
|
+
async delete(orgId, id) {
|
|
465
|
+
return deleteFn(
|
|
466
|
+
`${scimGroupsPath(orgId)}/${encodeURIComponent(id)}`
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
return { users, groups };
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// src/evidence-bundle.ts
|
|
474
|
+
function wireToBundle(w) {
|
|
475
|
+
return {
|
|
476
|
+
bundleId: w.bundle_id,
|
|
477
|
+
orgId: w.org_id,
|
|
478
|
+
incidentId: w.incident_id,
|
|
479
|
+
status: w.status,
|
|
480
|
+
includedPermits: w.included_permits ?? [],
|
|
481
|
+
includeOverrides: w.include_overrides ?? false,
|
|
482
|
+
format: w.format,
|
|
483
|
+
createdAt: w.created_at,
|
|
484
|
+
expiresAt: w.expires_at,
|
|
485
|
+
...w.download_url !== void 0 ? { downloadUrl: w.download_url } : {},
|
|
486
|
+
...w.metadata !== void 0 ? { metadata: w.metadata } : {}
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
function makeEvidenceBundleClient(postFn, getFn, getRawFn) {
|
|
490
|
+
return {
|
|
491
|
+
async list(params = {}) {
|
|
492
|
+
const qs = new URLSearchParams();
|
|
493
|
+
if (params.executionId !== void 0) qs.set("execution_id", params.executionId);
|
|
494
|
+
if (params.limit !== void 0) qs.set("limit", String(params.limit));
|
|
495
|
+
if (params.cursor !== void 0) qs.set("cursor", params.cursor);
|
|
496
|
+
const { body } = await getFn("/v1/evidence-bundles", qs.size > 0 ? qs : void 0);
|
|
497
|
+
return {
|
|
498
|
+
bundles: (body.bundles ?? []).map(wireToBundle),
|
|
499
|
+
nextCursor: body.next_cursor ?? null
|
|
500
|
+
};
|
|
501
|
+
},
|
|
502
|
+
async create(params) {
|
|
503
|
+
const payload = {
|
|
504
|
+
incident_id: params.incidentId
|
|
505
|
+
};
|
|
506
|
+
if (params.includedPermits !== void 0) {
|
|
507
|
+
payload["included_permits"] = params.includedPermits;
|
|
508
|
+
}
|
|
509
|
+
if (params.includeOverrides !== void 0) {
|
|
510
|
+
payload["include_overrides"] = params.includeOverrides;
|
|
511
|
+
}
|
|
512
|
+
const { body } = await postFn(
|
|
513
|
+
"/v1/evidence-bundles",
|
|
514
|
+
payload
|
|
515
|
+
);
|
|
516
|
+
return wireToBundle(body);
|
|
517
|
+
},
|
|
518
|
+
async get(bundleId) {
|
|
519
|
+
const { body } = await getFn(
|
|
520
|
+
`/v1/evidence-bundles/${encodeURIComponent(bundleId)}`
|
|
521
|
+
);
|
|
522
|
+
return wireToBundle(body);
|
|
523
|
+
},
|
|
524
|
+
async download(bundleId, format = "json") {
|
|
525
|
+
const qs = new URLSearchParams({ format });
|
|
526
|
+
const raw = await getRawFn(
|
|
527
|
+
`/v1/evidence-bundles/${encodeURIComponent(bundleId)}/download?${qs}`
|
|
528
|
+
);
|
|
529
|
+
return Buffer.from(raw);
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// src/auth.ts
|
|
535
|
+
function wireToTokenResponse(w) {
|
|
536
|
+
return {
|
|
537
|
+
accessToken: w.access_token,
|
|
538
|
+
refreshToken: w.refresh_token,
|
|
539
|
+
tokenType: w.token_type,
|
|
540
|
+
expiresIn: w.expires_in,
|
|
541
|
+
...w.scope !== void 0 ? { scope: w.scope } : {},
|
|
542
|
+
...w.idp_id !== void 0 ? { idpId: w.idp_id } : {}
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
function wireToIdpConnection(w) {
|
|
546
|
+
return {
|
|
547
|
+
id: w.id,
|
|
548
|
+
name: w.name,
|
|
549
|
+
provider: w.provider,
|
|
550
|
+
enabled: w.enabled,
|
|
551
|
+
isDefault: w.default,
|
|
552
|
+
...w.domains !== void 0 ? { domains: w.domains } : {},
|
|
553
|
+
createdAt: w.created_at
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
function makeAuthClient(postFn, getFn) {
|
|
557
|
+
return {
|
|
558
|
+
async refresh(refreshToken) {
|
|
559
|
+
const { body } = await postFn(
|
|
560
|
+
"/v1/auth/token/refresh",
|
|
561
|
+
{ refresh_token: refreshToken, grant_type: "refresh_token" }
|
|
562
|
+
);
|
|
563
|
+
return wireToTokenResponse(body);
|
|
564
|
+
},
|
|
565
|
+
async refreshWithIdp(idpId, refreshToken) {
|
|
566
|
+
const path = `/v1/auth/idp/${encodeURIComponent(idpId)}/token/refresh`;
|
|
567
|
+
const { body } = await postFn(path, {
|
|
568
|
+
refresh_token: refreshToken,
|
|
569
|
+
grant_type: "refresh_token",
|
|
570
|
+
idp_id: idpId
|
|
571
|
+
});
|
|
572
|
+
return wireToTokenResponse(body);
|
|
573
|
+
},
|
|
574
|
+
async listIdpConnections() {
|
|
575
|
+
const { body } = await getFn(
|
|
576
|
+
"/v1/auth/idp-connections"
|
|
577
|
+
);
|
|
578
|
+
return (body.connections ?? []).map(wireToIdpConnection);
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// src/sso.ts
|
|
584
|
+
function wireToSsoConnection(w) {
|
|
585
|
+
return {
|
|
586
|
+
id: w.id,
|
|
587
|
+
organizationId: w.organization_id,
|
|
588
|
+
name: w.name,
|
|
589
|
+
protocol: w.protocol,
|
|
590
|
+
idpEntityId: w.idp_entity_id,
|
|
591
|
+
metadataUrl: w.metadata_url,
|
|
592
|
+
metadataXml: w.metadata_xml,
|
|
593
|
+
emailDomain: w.email_domain,
|
|
594
|
+
enforceForDomain: w.enforce_for_domain,
|
|
595
|
+
isActive: w.is_active,
|
|
596
|
+
supabaseProviderId: w.supabase_provider_id,
|
|
597
|
+
createdBy: w.created_by,
|
|
598
|
+
createdAt: w.created_at,
|
|
599
|
+
updatedAt: w.updated_at
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
function wireToSsoJitRule(w) {
|
|
603
|
+
return {
|
|
604
|
+
id: w.id,
|
|
605
|
+
connectionId: w.connection_id,
|
|
606
|
+
organizationId: w.organization_id,
|
|
607
|
+
claimAttribute: w.claim_attribute,
|
|
608
|
+
claimValue: w.claim_value,
|
|
609
|
+
grantedRole: w.granted_role,
|
|
610
|
+
precedence: w.precedence,
|
|
611
|
+
isActive: w.is_active,
|
|
612
|
+
createdAt: w.created_at,
|
|
613
|
+
updatedAt: w.updated_at
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
function wireToSsoReadiness(w) {
|
|
617
|
+
return {
|
|
618
|
+
connectionConfigured: w.connection_configured,
|
|
619
|
+
connectionTested: w.connection_tested,
|
|
620
|
+
breakGlassSet: w.break_glass_set,
|
|
621
|
+
serviceApiKeysReviewed: w.service_api_keys_reviewed
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
function ssoConnectionInputToWire(input) {
|
|
625
|
+
const w = {};
|
|
626
|
+
if (input.name !== void 0) w["name"] = input.name;
|
|
627
|
+
if (input.protocol !== void 0) w["protocol"] = input.protocol;
|
|
628
|
+
if (input.idpEntityId !== void 0) w["idp_entity_id"] = input.idpEntityId;
|
|
629
|
+
if (input.metadataUrl !== void 0) w["metadata_url"] = input.metadataUrl;
|
|
630
|
+
if (input.metadataXml !== void 0) w["metadata_xml"] = input.metadataXml;
|
|
631
|
+
if (input.emailDomain !== void 0) w["email_domain"] = input.emailDomain;
|
|
632
|
+
if (input.enforceForDomain !== void 0) w["enforce_for_domain"] = input.enforceForDomain;
|
|
633
|
+
return w;
|
|
634
|
+
}
|
|
635
|
+
function makeSsoClient(getFn, postFn, patchFn, deleteFn) {
|
|
636
|
+
return {
|
|
637
|
+
async listConnections() {
|
|
638
|
+
const { body } = await getFn("/v1/sso/connections");
|
|
639
|
+
return { connections: (body.connections ?? []).map(wireToSsoConnection) };
|
|
640
|
+
},
|
|
641
|
+
async getConnection(id) {
|
|
642
|
+
const { body } = await getFn(`/v1/sso/connections/${encodeURIComponent(id)}`);
|
|
643
|
+
return wireToSsoConnection(body);
|
|
644
|
+
},
|
|
645
|
+
async createConnection(input) {
|
|
646
|
+
const { body } = await postFn("/v1/sso/connections", ssoConnectionInputToWire(input));
|
|
647
|
+
return wireToSsoConnection(body);
|
|
648
|
+
},
|
|
649
|
+
async updateConnection(id, input) {
|
|
650
|
+
const { body } = await patchFn(
|
|
651
|
+
`/v1/sso/connections/${encodeURIComponent(id)}`,
|
|
652
|
+
ssoConnectionInputToWire(input)
|
|
653
|
+
);
|
|
654
|
+
return wireToSsoConnection(body);
|
|
655
|
+
},
|
|
656
|
+
async deleteConnection(id) {
|
|
657
|
+
await deleteFn(`/v1/sso/connections/${encodeURIComponent(id)}`);
|
|
658
|
+
},
|
|
659
|
+
async activateConnection(id) {
|
|
660
|
+
const { body } = await postFn(
|
|
661
|
+
`/v1/sso/connections/${encodeURIComponent(id)}/activate`,
|
|
662
|
+
{}
|
|
663
|
+
);
|
|
664
|
+
return { ok: body.ok, supabaseProviderId: body.supabase_provider_id };
|
|
665
|
+
},
|
|
666
|
+
async enforce(action) {
|
|
667
|
+
const { body } = await postFn("/v1/sso/enforce", { action });
|
|
668
|
+
return {
|
|
669
|
+
ok: body.ok,
|
|
670
|
+
action: body.action,
|
|
671
|
+
enforceSso: body.enforce_sso,
|
|
672
|
+
enforceSsoAt: body.enforce_sso_at
|
|
673
|
+
};
|
|
674
|
+
},
|
|
675
|
+
async getStatus() {
|
|
676
|
+
const { body } = await getFn("/v1/sso/status");
|
|
677
|
+
return wireToSsoReadiness(body.readiness);
|
|
678
|
+
},
|
|
679
|
+
async listJitRules(connectionId) {
|
|
680
|
+
const qs = connectionId ? new URLSearchParams({ connection_id: connectionId }) : void 0;
|
|
681
|
+
const { body } = await getFn("/v1/sso/jit-rules", qs);
|
|
682
|
+
return { rules: (body.rules ?? []).map(wireToSsoJitRule) };
|
|
683
|
+
},
|
|
684
|
+
async createJitRule(input) {
|
|
685
|
+
const payload = {
|
|
686
|
+
connection_id: input.connectionId,
|
|
687
|
+
claim_attribute: input.claimAttribute,
|
|
688
|
+
claim_value: input.claimValue,
|
|
689
|
+
granted_role: input.grantedRole
|
|
690
|
+
};
|
|
691
|
+
if (input.precedence !== void 0) payload["precedence"] = input.precedence;
|
|
692
|
+
const { body } = await postFn("/v1/sso/jit-rules", payload);
|
|
693
|
+
return wireToSsoJitRule(body);
|
|
694
|
+
},
|
|
695
|
+
async patchJitRule(id, patch) {
|
|
696
|
+
const payload = {};
|
|
697
|
+
if (patch.claimAttribute !== void 0) payload["claim_attribute"] = patch.claimAttribute;
|
|
698
|
+
if (patch.claimValue !== void 0) payload["claim_value"] = patch.claimValue;
|
|
699
|
+
if (patch.grantedRole !== void 0) payload["granted_role"] = patch.grantedRole;
|
|
700
|
+
if (patch.precedence !== void 0) payload["precedence"] = patch.precedence;
|
|
701
|
+
if (patch.isActive !== void 0) payload["is_active"] = patch.isActive;
|
|
702
|
+
const { body } = await patchFn(
|
|
703
|
+
`/v1/sso/jit-rules/${encodeURIComponent(id)}`,
|
|
704
|
+
payload
|
|
705
|
+
);
|
|
706
|
+
return wireToSsoJitRule(body);
|
|
707
|
+
},
|
|
708
|
+
async deleteJitRule(id) {
|
|
709
|
+
await deleteFn(`/v1/sso/jit-rules/${encodeURIComponent(id)}`);
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// src/access-governance-log.ts
|
|
715
|
+
function wireToEvent(w) {
|
|
716
|
+
return {
|
|
717
|
+
id: w.id,
|
|
718
|
+
eventType: w.event_type,
|
|
719
|
+
orgId: w.org_id,
|
|
720
|
+
actorId: w.actor_id,
|
|
721
|
+
actorEmail: w.actor_email,
|
|
722
|
+
ipAddress: w.ip_address,
|
|
723
|
+
metadata: w.metadata ?? {},
|
|
724
|
+
createdAt: w.created_at
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
function makeAccessGovernanceLogClient(getFn) {
|
|
728
|
+
return {
|
|
729
|
+
async list(query = {}) {
|
|
730
|
+
const qs = new URLSearchParams();
|
|
731
|
+
if (query.limit !== void 0) qs.set("limit", String(query.limit));
|
|
732
|
+
if (query.cursor) qs.set("cursor", query.cursor);
|
|
733
|
+
if (query.eventType) qs.set("event_type", query.eventType);
|
|
734
|
+
if (query.actorId) qs.set("actor_id", query.actorId);
|
|
735
|
+
if (query.from) qs.set("from", query.from);
|
|
736
|
+
if (query.to) qs.set("to", query.to);
|
|
737
|
+
const { body } = await getFn(
|
|
738
|
+
"/v1/access-governance-log",
|
|
739
|
+
qs.size > 0 ? qs : void 0
|
|
740
|
+
);
|
|
741
|
+
return {
|
|
742
|
+
events: (body.events ?? []).map(wireToEvent),
|
|
743
|
+
nextCursor: body.next_cursor,
|
|
744
|
+
totalCount: body.total_count ?? 0
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
|
|
197
750
|
// src/client.ts
|
|
198
751
|
var DEFAULT_BASE_URL = "https://api.atlasent.io";
|
|
199
752
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
200
|
-
var SDK_VERSION = "2.
|
|
753
|
+
var SDK_VERSION = "2.10.0";
|
|
754
|
+
var warnedBrowser = false;
|
|
755
|
+
var V1_EVALUATE_BATCH_PATH = "/v1/evaluate/batch";
|
|
756
|
+
var V1_EVALUATE_BATCH_LEGACY_PATH = "/v1-evaluate-batch";
|
|
757
|
+
var V1_EVALUATE_STREAM_PATH = "/v1/evaluate/stream";
|
|
758
|
+
var V1_EVALUATE_STREAM_LEGACY_PATH = "/v1-evaluate-stream";
|
|
201
759
|
function _buildUserAgent() {
|
|
202
760
|
const isNode2 = typeof process !== "undefined" && typeof process?.versions?.node === "string";
|
|
203
761
|
return isNode2 ? `@atlasent/sdk/${SDK_VERSION} node/${process.version}` : `@atlasent/sdk/${SDK_VERSION} browser`;
|
|
@@ -261,6 +819,18 @@ var AtlaSentClient = class {
|
|
|
261
819
|
fetchImpl;
|
|
262
820
|
userAgent;
|
|
263
821
|
retryPolicy;
|
|
822
|
+
/** SCIM 2.0 provisioning sub-client. Access as `client.scim`. */
|
|
823
|
+
scim;
|
|
824
|
+
/** Evidence bundle sub-client. Access as `client.evidenceBundles`. */
|
|
825
|
+
evidenceBundles;
|
|
826
|
+
/** Auth / token management sub-client. Access as `client.auth`. */
|
|
827
|
+
auth;
|
|
828
|
+
/** SSO administration sub-client. Access as `client.sso`. */
|
|
829
|
+
sso;
|
|
830
|
+
/** Access governance log sub-client. Access as `client.accessGovernanceLog`. */
|
|
831
|
+
accessGovernanceLog;
|
|
832
|
+
/** Trust-root snapshot manager for this client instance. */
|
|
833
|
+
trustRoot;
|
|
264
834
|
constructor(options) {
|
|
265
835
|
if (!options.apiKey || typeof options.apiKey !== "string") {
|
|
266
836
|
throw new AtlaSentError("apiKey is required", {
|
|
@@ -273,6 +843,12 @@ var AtlaSentClient = class {
|
|
|
273
843
|
{ code: "network" }
|
|
274
844
|
);
|
|
275
845
|
}
|
|
846
|
+
if (!warnedBrowser && typeof globalThis["window"] !== "undefined" && typeof process === "undefined") {
|
|
847
|
+
warnedBrowser = true;
|
|
848
|
+
console.warn(
|
|
849
|
+
"[@atlasent/sdk] Running in a browser environment. API keys should not be exposed in client-side bundles. Use a server-side proxy instead."
|
|
850
|
+
);
|
|
851
|
+
}
|
|
276
852
|
this.apiKey = _validateApiKey(options.apiKey);
|
|
277
853
|
this.baseUrl = _enforceTls(options.baseUrl ?? DEFAULT_BASE_URL).replace(
|
|
278
854
|
/\/+$/,
|
|
@@ -282,6 +858,44 @@ var AtlaSentClient = class {
|
|
|
282
858
|
this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
283
859
|
this.userAgent = _buildUserAgent();
|
|
284
860
|
this.retryPolicy = mergePolicy(options.retryPolicy ?? {});
|
|
861
|
+
this.scim = makeScimClient(
|
|
862
|
+
(path, body, query) => this._post(path, body, query),
|
|
863
|
+
(path, query) => this._get(path, query),
|
|
864
|
+
(path, body) => this._put(path, body),
|
|
865
|
+
(path) => this._delete(path)
|
|
866
|
+
);
|
|
867
|
+
this.evidenceBundles = makeEvidenceBundleClient(
|
|
868
|
+
(path, body) => this._post(path, body),
|
|
869
|
+
(path, query) => this._get(path, query),
|
|
870
|
+
(path) => this._getRaw(path)
|
|
871
|
+
);
|
|
872
|
+
this.auth = makeAuthClient(
|
|
873
|
+
(path, body) => this._post(path, body),
|
|
874
|
+
(path) => this._get(path)
|
|
875
|
+
);
|
|
876
|
+
this.sso = makeSsoClient(
|
|
877
|
+
(path, query) => this._get(path, query),
|
|
878
|
+
(path, body) => this._post(path, body),
|
|
879
|
+
(path, body) => this._patch(path, body),
|
|
880
|
+
(path) => this._delete(path)
|
|
881
|
+
);
|
|
882
|
+
this.accessGovernanceLog = makeAccessGovernanceLogClient(
|
|
883
|
+
(path, query) => this._get(path, query)
|
|
884
|
+
);
|
|
885
|
+
if (options.trustRootUrl !== void 0 || options.trustSnapshotRefreshMs !== void 0) {
|
|
886
|
+
const globalSnap = getGlobalTrustRootManager({ disableRefresh: true }).getSnapshot();
|
|
887
|
+
this.trustRoot = new TrustRootManager(globalSnap, {
|
|
888
|
+
...options.trustRootUrl !== void 0 && { refreshBaseUrl: options.trustRootUrl },
|
|
889
|
+
...options.trustSnapshotRefreshMs !== void 0 && { refreshIntervalMs: options.trustSnapshotRefreshMs }
|
|
890
|
+
});
|
|
891
|
+
} else {
|
|
892
|
+
this.trustRoot = getGlobalTrustRootManager();
|
|
893
|
+
}
|
|
894
|
+
this.trustRoot.checkExpiry();
|
|
895
|
+
}
|
|
896
|
+
/** Return the current trust-root snapshot (pinned or last successful refresh). */
|
|
897
|
+
getTrustSnapshot() {
|
|
898
|
+
return this.trustRoot.getSnapshot();
|
|
285
899
|
}
|
|
286
900
|
/**
|
|
287
901
|
* Ask the policy engine whether an agent action is permitted.
|
|
@@ -308,6 +922,11 @@ var AtlaSentClient = class {
|
|
|
308
922
|
context: normalized.context ?? {}
|
|
309
923
|
};
|
|
310
924
|
if (normalized.explain !== void 0) body.explain = normalized.explain;
|
|
925
|
+
if (normalized.environment !== void 0) body.environment = normalized.environment;
|
|
926
|
+
if (normalized.resource !== void 0) body.resource = normalized.resource;
|
|
927
|
+
if (normalized.current_state !== void 0) body.current_state = normalized.current_state;
|
|
928
|
+
if (normalized.proposed_state !== void 0) body.proposed_state = normalized.proposed_state;
|
|
929
|
+
if (normalized.execution_binding !== void 0) body.execution_binding = normalized.execution_binding;
|
|
311
930
|
const { body: wire, rateLimit } = await this.post(
|
|
312
931
|
"/v1-evaluate",
|
|
313
932
|
body
|
|
@@ -354,13 +973,25 @@ var AtlaSentClient = class {
|
|
|
354
973
|
hardBlocks: wire.risk_envelope.hard_blocks ?? [],
|
|
355
974
|
...wire.risk_envelope.factors && { factors: wire.risk_envelope.factors }
|
|
356
975
|
}
|
|
357
|
-
}
|
|
976
|
+
},
|
|
977
|
+
...wire.risk_class !== void 0 && { riskClass: wire.risk_class },
|
|
978
|
+
...wire.authority_basis && {
|
|
979
|
+
authorityBasis: {
|
|
980
|
+
kind: wire.authority_basis.kind,
|
|
981
|
+
...wire.authority_basis.reference !== void 0 && { reference: wire.authority_basis.reference },
|
|
982
|
+
...wire.authority_basis.granted_by !== void 0 && { grantedBy: wire.authority_basis.granted_by },
|
|
983
|
+
...wire.authority_basis.rationale !== void 0 && { rationale: wire.authority_basis.rationale },
|
|
984
|
+
...wire.authority_basis.expires_at !== void 0 && { expiresAt: wire.authority_basis.expires_at }
|
|
985
|
+
}
|
|
986
|
+
},
|
|
987
|
+
...wire.escalation_id !== void 0 && { escalationId: wire.escalation_id }
|
|
358
988
|
};
|
|
359
989
|
}
|
|
360
990
|
/**
|
|
361
991
|
* Batch evaluate — send up to 100 decisions in a single round-trip.
|
|
362
992
|
*
|
|
363
|
-
|
|
993
|
+
* Wraps `POST /v1/evaluate/batch` (with fallback to
|
|
994
|
+
* `POST /v1-evaluate-batch` on older runtimes). The server evaluates each item
|
|
364
995
|
* against the active policy bundle and returns results in the same
|
|
365
996
|
* order as the input. One rate-limit token is consumed for the
|
|
366
997
|
* whole batch, and one audit-chain entry lists every included
|
|
@@ -398,8 +1029,9 @@ var AtlaSentClient = class {
|
|
|
398
1029
|
}));
|
|
399
1030
|
const wireBody = { items: wireItems };
|
|
400
1031
|
if (batchId) wireBody.batch_id = batchId;
|
|
401
|
-
const { body: wire, rateLimit } = await this.
|
|
402
|
-
|
|
1032
|
+
const { body: wire, rateLimit } = await this.postWithPathFallback(
|
|
1033
|
+
V1_EVALUATE_BATCH_PATH,
|
|
1034
|
+
V1_EVALUATE_BATCH_LEGACY_PATH,
|
|
403
1035
|
wireBody
|
|
404
1036
|
);
|
|
405
1037
|
const items = (wire.items ?? []).map(
|
|
@@ -692,6 +1324,7 @@ var AtlaSentClient = class {
|
|
|
692
1324
|
const agent = input.agent ?? "ci-deploy-bot";
|
|
693
1325
|
const action = input.action ?? PRODUCTION_DEPLOY_ACTION;
|
|
694
1326
|
const context = input.context ?? {};
|
|
1327
|
+
const environment = typeof context.environment === "string" ? context.environment : typeof context.environment_name === "string" ? context.environment_name : void 0;
|
|
695
1328
|
const evaluation = await this.evaluate({ agent, action, context });
|
|
696
1329
|
if (evaluation.decision !== "allow") {
|
|
697
1330
|
return {
|
|
@@ -708,7 +1341,8 @@ var AtlaSentClient = class {
|
|
|
708
1341
|
permitId: evaluation.permitId,
|
|
709
1342
|
agent,
|
|
710
1343
|
action,
|
|
711
|
-
context
|
|
1344
|
+
context,
|
|
1345
|
+
...environment !== void 0 ? { environment } : {}
|
|
712
1346
|
});
|
|
713
1347
|
if (!verification.verified) {
|
|
714
1348
|
return {
|
|
@@ -922,15 +1556,15 @@ var AtlaSentClient = class {
|
|
|
922
1556
|
*/
|
|
923
1557
|
async keySelf() {
|
|
924
1558
|
const { body: wire, rateLimit } = await this.get("/v1-api-key-self");
|
|
925
|
-
if (typeof wire.key_id !== "string" || typeof wire.
|
|
1559
|
+
if (typeof wire.key_id !== "string" || typeof wire.org_id !== "string") {
|
|
926
1560
|
throw new AtlaSentError(
|
|
927
|
-
"Malformed response from /v1-api-key-self: missing `key_id` or `
|
|
1561
|
+
"Malformed response from /v1-api-key-self: missing `key_id` or `org_id`",
|
|
928
1562
|
{ code: "bad_response" }
|
|
929
1563
|
);
|
|
930
1564
|
}
|
|
931
1565
|
return {
|
|
932
1566
|
keyId: wire.key_id,
|
|
933
|
-
|
|
1567
|
+
orgId: wire.org_id,
|
|
934
1568
|
environment: wire.environment,
|
|
935
1569
|
scopes: wire.scopes ?? [],
|
|
936
1570
|
allowedCidrs: wire.allowed_cidrs ?? null,
|
|
@@ -1142,7 +1776,8 @@ var AtlaSentClient = class {
|
|
|
1142
1776
|
return response;
|
|
1143
1777
|
}
|
|
1144
1778
|
/**
|
|
1145
|
-
|
|
1779
|
+
* Open a streaming evaluation session against `POST /v1/evaluate/stream`
|
|
1780
|
+
* (with fallback to `POST /v1-evaluate-stream` on older runtimes).
|
|
1146
1781
|
*
|
|
1147
1782
|
* Yields {@link StreamDecisionEvent} and {@link StreamProgressEvent} objects
|
|
1148
1783
|
* as the server emits them. The iterator ends cleanly when the server sends
|
|
@@ -1180,7 +1815,7 @@ var AtlaSentClient = class {
|
|
|
1180
1815
|
api_key: this.apiKey
|
|
1181
1816
|
};
|
|
1182
1817
|
const requestId = globalThis.crypto.randomUUID();
|
|
1183
|
-
|
|
1818
|
+
let streamPath = V1_EVALUATE_STREAM_PATH;
|
|
1184
1819
|
let lastEventId;
|
|
1185
1820
|
let retryCount = 0;
|
|
1186
1821
|
while (true) {
|
|
@@ -1200,7 +1835,7 @@ var AtlaSentClient = class {
|
|
|
1200
1835
|
const signal = opts.signal ? AbortSignal.any([connectionTimeoutSignal, opts.signal]) : connectionTimeoutSignal;
|
|
1201
1836
|
let response;
|
|
1202
1837
|
try {
|
|
1203
|
-
response = await this.fetchImpl(
|
|
1838
|
+
response = await this.fetchImpl(`${this.baseUrl}${streamPath}`, {
|
|
1204
1839
|
method: "POST",
|
|
1205
1840
|
headers,
|
|
1206
1841
|
body: JSON.stringify(body),
|
|
@@ -1216,6 +1851,10 @@ var AtlaSentClient = class {
|
|
|
1216
1851
|
throw mapped;
|
|
1217
1852
|
}
|
|
1218
1853
|
if (!response.ok) {
|
|
1854
|
+
if (streamPath === V1_EVALUATE_STREAM_PATH && (response.status === 404 || response.status === 405)) {
|
|
1855
|
+
streamPath = V1_EVALUATE_STREAM_LEGACY_PATH;
|
|
1856
|
+
continue;
|
|
1857
|
+
}
|
|
1219
1858
|
throw await buildHttpError(response, requestId);
|
|
1220
1859
|
}
|
|
1221
1860
|
if (!response.body) {
|
|
@@ -1267,6 +1906,16 @@ var AtlaSentClient = class {
|
|
|
1267
1906
|
async post(path, body, query) {
|
|
1268
1907
|
return this.request(path, "POST", body, query);
|
|
1269
1908
|
}
|
|
1909
|
+
async postWithPathFallback(primaryPath, fallbackPath, body, query) {
|
|
1910
|
+
try {
|
|
1911
|
+
return await this.post(primaryPath, body, query);
|
|
1912
|
+
} catch (err) {
|
|
1913
|
+
if (err instanceof AtlaSentError && (err.status === 404 || err.status === 405)) {
|
|
1914
|
+
return this.post(fallbackPath, body, query);
|
|
1915
|
+
}
|
|
1916
|
+
throw err;
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1270
1919
|
async get(path, query) {
|
|
1271
1920
|
return this.request(path, "GET", void 0, query);
|
|
1272
1921
|
}
|
|
@@ -1959,6 +2608,82 @@ var AtlaSentClient = class {
|
|
|
1959
2608
|
);
|
|
1960
2609
|
return [...body.evaluations ?? []];
|
|
1961
2610
|
}
|
|
2611
|
+
// ── Private adapters for sub-client factories ──────────────────────────────
|
|
2612
|
+
// Thin wrappers that expose the private request infrastructure to sub-client
|
|
2613
|
+
// factories (scim, evidenceBundles, auth) without widening the public API.
|
|
2614
|
+
async _post(path, body, query) {
|
|
2615
|
+
const { body: b } = await this.post(path, body, query);
|
|
2616
|
+
return { body: b };
|
|
2617
|
+
}
|
|
2618
|
+
async _get(path, query) {
|
|
2619
|
+
const { body: b } = await this.get(path, query);
|
|
2620
|
+
return { body: b };
|
|
2621
|
+
}
|
|
2622
|
+
async _put(path, body) {
|
|
2623
|
+
return this._requestRaw(path, "PUT", body, void 0);
|
|
2624
|
+
}
|
|
2625
|
+
async _patch(path, body) {
|
|
2626
|
+
return this._requestRaw(path, "PATCH", body, void 0);
|
|
2627
|
+
}
|
|
2628
|
+
async _delete(path) {
|
|
2629
|
+
await this._requestRaw(path, "DELETE", void 0, void 0);
|
|
2630
|
+
}
|
|
2631
|
+
async _getRaw(path) {
|
|
2632
|
+
const url = `${this.baseUrl}${path}`;
|
|
2633
|
+
const requestId = globalThis.crypto.randomUUID();
|
|
2634
|
+
const headers = {
|
|
2635
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
2636
|
+
"User-Agent": this.userAgent,
|
|
2637
|
+
"X-Request-ID": requestId,
|
|
2638
|
+
"X-AtlaSent-Protocol-Version": "1"
|
|
2639
|
+
};
|
|
2640
|
+
const response = await this.fetchImpl(url, {
|
|
2641
|
+
method: "GET",
|
|
2642
|
+
headers,
|
|
2643
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
2644
|
+
});
|
|
2645
|
+
if (!response.ok) {
|
|
2646
|
+
const text = await response.text().catch(() => "");
|
|
2647
|
+
throw new AtlaSentError(`GET ${path} returned ${response.status}`, {
|
|
2648
|
+
code: response.status >= 500 ? "server_error" : "bad_request",
|
|
2649
|
+
status: response.status,
|
|
2650
|
+
requestId
|
|
2651
|
+
});
|
|
2652
|
+
}
|
|
2653
|
+
return response.arrayBuffer();
|
|
2654
|
+
}
|
|
2655
|
+
async _requestRaw(path, method, body, query) {
|
|
2656
|
+
const qs = query && Array.from(query).length > 0 ? `?${query.toString()}` : "";
|
|
2657
|
+
const url = `${this.baseUrl}${path}${qs}`;
|
|
2658
|
+
const requestId = globalThis.crypto.randomUUID();
|
|
2659
|
+
const headers = {
|
|
2660
|
+
Accept: "application/json",
|
|
2661
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
2662
|
+
"User-Agent": this.userAgent,
|
|
2663
|
+
"X-Request-ID": requestId,
|
|
2664
|
+
"X-AtlaSent-Protocol-Version": "1"
|
|
2665
|
+
};
|
|
2666
|
+
if (method === "PUT" && body !== void 0) {
|
|
2667
|
+
headers["Content-Type"] = "application/json";
|
|
2668
|
+
}
|
|
2669
|
+
const init = { method, headers, signal: AbortSignal.timeout(this.timeoutMs) };
|
|
2670
|
+
if (method === "PUT" && body !== void 0) {
|
|
2671
|
+
init.body = JSON.stringify(body);
|
|
2672
|
+
}
|
|
2673
|
+
const response = await this.fetchImpl(url, init);
|
|
2674
|
+
if (!response.ok) {
|
|
2675
|
+
const text = await response.text().catch(() => "");
|
|
2676
|
+
throw new AtlaSentError(`${method} ${path} returned ${response.status}`, {
|
|
2677
|
+
code: response.status >= 500 ? "server_error" : "bad_request",
|
|
2678
|
+
status: response.status,
|
|
2679
|
+
requestId
|
|
2680
|
+
});
|
|
2681
|
+
}
|
|
2682
|
+
if (method === "DELETE") {
|
|
2683
|
+
return { body: {} };
|
|
2684
|
+
}
|
|
2685
|
+
return { body: await response.json() };
|
|
2686
|
+
}
|
|
1962
2687
|
};
|
|
1963
2688
|
function parseRateLimitHeaders(headers) {
|
|
1964
2689
|
const rawLimit = headers.get("x-ratelimit-limit");
|
|
@@ -2104,7 +2829,7 @@ function buildAuditEventsQuery(query) {
|
|
|
2104
2829
|
return params;
|
|
2105
2830
|
}
|
|
2106
2831
|
function sleep(ms) {
|
|
2107
|
-
return new Promise((
|
|
2832
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
2108
2833
|
}
|
|
2109
2834
|
function parseRetryAfter(raw) {
|
|
2110
2835
|
if (!raw) return void 0;
|
|
@@ -2125,14 +2850,14 @@ async function* parseSseStream(body, requestId, timeoutMs, onEventId) {
|
|
|
2125
2850
|
if (timeoutMs <= 0) {
|
|
2126
2851
|
return reader.read();
|
|
2127
2852
|
}
|
|
2128
|
-
return new Promise((
|
|
2853
|
+
return new Promise((resolve3, reject) => {
|
|
2129
2854
|
const timer = setTimeout(() => {
|
|
2130
2855
|
reject(new StreamTimeoutError(timeoutMs));
|
|
2131
2856
|
}, timeoutMs);
|
|
2132
2857
|
reader.read().then(
|
|
2133
2858
|
(result) => {
|
|
2134
2859
|
clearTimeout(timer);
|
|
2135
|
-
|
|
2860
|
+
resolve3(result);
|
|
2136
2861
|
},
|
|
2137
2862
|
(err) => {
|
|
2138
2863
|
clearTimeout(timer);
|
|
@@ -2305,6 +3030,15 @@ async function protect(request) {
|
|
|
2305
3030
|
{ code: "bad_request" }
|
|
2306
3031
|
);
|
|
2307
3032
|
}
|
|
3033
|
+
const trustMgr = getGlobalTrustRootManager({ disableRefresh: false });
|
|
3034
|
+
if (trustMgr.checkExpiry() === "expired") {
|
|
3035
|
+
const snap = trustMgr.getSnapshot();
|
|
3036
|
+
throw new BundleVerificationError({
|
|
3037
|
+
reason: "trust_snapshot_expired",
|
|
3038
|
+
snapshotValidUntil: snap.valid_until,
|
|
3039
|
+
snapshotFetchedAt: snap.issued_at
|
|
3040
|
+
});
|
|
3041
|
+
}
|
|
2308
3042
|
const client = getClient();
|
|
2309
3043
|
const evaluation = await client.evaluate(request);
|
|
2310
3044
|
if (evaluation.decision !== "allow") {
|
|
@@ -2359,15 +3093,15 @@ async function protect(request) {
|
|
|
2359
3093
|
|
|
2360
3094
|
// src/hono.ts
|
|
2361
3095
|
var DEFAULT_CONTEXT_KEY = "atlasent";
|
|
2362
|
-
async function
|
|
3096
|
+
async function resolve2(value, c) {
|
|
2363
3097
|
return typeof value === "function" ? await value(c) : value;
|
|
2364
3098
|
}
|
|
2365
3099
|
function atlaSentGuard(options) {
|
|
2366
3100
|
const contextKey = options.key ?? DEFAULT_CONTEXT_KEY;
|
|
2367
3101
|
return async (c, next) => {
|
|
2368
3102
|
const [agent, action, ctx] = await Promise.all([
|
|
2369
|
-
|
|
2370
|
-
|
|
3103
|
+
resolve2(options.agent, c),
|
|
3104
|
+
resolve2(options.action, c),
|
|
2371
3105
|
options.context ? options.context(c) : Promise.resolve(void 0)
|
|
2372
3106
|
]);
|
|
2373
3107
|
const request = { agent, action };
|