@oneidentity/safeguard 7.4.0 → 8.0.0-pre36948

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/index.cjs ADDED
@@ -0,0 +1,1248 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region \0rolldown/runtime.js
3
+ var __defProp = Object.defineProperty;
4
+ var __exportAll = (all, no_symbols) => {
5
+ let target = {};
6
+ for (var name in all) __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: true
9
+ });
10
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
11
+ return target;
12
+ };
13
+ //#endregion
14
+ let undici = require("undici");
15
+ //#region src/types.ts
16
+ /**
17
+ * Safeguard appliance service endpoints.
18
+ */
19
+ let Service = /* @__PURE__ */ function(Service) {
20
+ Service["CORE"] = "core";
21
+ Service["APPLIANCE"] = "appliance";
22
+ Service["NOTIFICATION"] = "notification";
23
+ Service["A2A"] = "a2a";
24
+ Service["EVENT"] = "event";
25
+ Service["RSTS"] = "RSTS";
26
+ return Service;
27
+ }({});
28
+ /**
29
+ * HTTP methods supported by the Safeguard API.
30
+ */
31
+ let HttpMethod = /* @__PURE__ */ function(HttpMethod) {
32
+ HttpMethod["GET"] = "GET";
33
+ HttpMethod["POST"] = "POST";
34
+ HttpMethod["PUT"] = "PUT";
35
+ HttpMethod["DELETE"] = "DELETE";
36
+ HttpMethod["PATCH"] = "PATCH";
37
+ return HttpMethod;
38
+ }({});
39
+ //#endregion
40
+ //#region src/errors.ts
41
+ var errors_exports = /* @__PURE__ */ __exportAll({
42
+ ApiError: () => ApiError,
43
+ AuthenticationError: () => AuthenticationError,
44
+ AuthorizationError: () => AuthorizationError,
45
+ ConfigurationError: () => ConfigurationError,
46
+ NotFoundError: () => NotFoundError,
47
+ SafeguardError: () => SafeguardError,
48
+ TransportError: () => TransportError
49
+ });
50
+ /**
51
+ * Error hierarchy for the Safeguard SDK.
52
+ *
53
+ * SafeguardError
54
+ * ├── ApiError (HTTP errors from Safeguard API)
55
+ * │ ├── AuthenticationError (401)
56
+ * │ ├── AuthorizationError (403)
57
+ * │ └── NotFoundError (404)
58
+ * ├── TransportError (Network/connection failures)
59
+ * └── ConfigurationError (Invalid client options)
60
+ */
61
+ var SafeguardError = class extends Error {
62
+ constructor(message, options) {
63
+ super(message, options);
64
+ this.name = "SafeguardError";
65
+ }
66
+ };
67
+ var ApiError = class ApiError extends SafeguardError {
68
+ status;
69
+ body;
70
+ constructor(message, status, body = null, options) {
71
+ super(message, options);
72
+ this.name = "ApiError";
73
+ this.status = status;
74
+ this.body = body;
75
+ }
76
+ /**
77
+ * Creates an appropriate ApiError subclass based on HTTP status code.
78
+ */
79
+ static fromResponse(status, body) {
80
+ const parsed = ApiError.parseBody(body);
81
+ const message = ApiError.buildMessage(status, parsed);
82
+ switch (status) {
83
+ case 401: return new AuthenticationError(message, parsed);
84
+ case 403: return new AuthorizationError(message, parsed);
85
+ case 404: return new NotFoundError(message, parsed);
86
+ default: return new ApiError(message, status, parsed);
87
+ }
88
+ }
89
+ static parseBody(body) {
90
+ if (body === null || body === void 0) return null;
91
+ if (typeof body === "object") return body;
92
+ try {
93
+ return JSON.parse(body);
94
+ } catch {
95
+ return { Message: body };
96
+ }
97
+ }
98
+ static buildMessage(status, body) {
99
+ const apiMessage = body?.Message ?? body?.InnerError?.Message;
100
+ if (apiMessage) return `HTTP ${String(status)}: ${apiMessage}`;
101
+ return `HTTP ${String(status)}`;
102
+ }
103
+ };
104
+ var AuthenticationError = class extends ApiError {
105
+ constructor(message, body = null, options) {
106
+ super(message, 401, body, options);
107
+ this.name = "AuthenticationError";
108
+ }
109
+ };
110
+ var AuthorizationError = class extends ApiError {
111
+ constructor(message, body = null, options) {
112
+ super(message, 403, body, options);
113
+ this.name = "AuthorizationError";
114
+ }
115
+ };
116
+ var NotFoundError = class extends ApiError {
117
+ constructor(message, body = null, options) {
118
+ super(message, 404, body, options);
119
+ this.name = "NotFoundError";
120
+ }
121
+ };
122
+ var TransportError = class extends SafeguardError {
123
+ constructor(message, options) {
124
+ super(message, options);
125
+ this.name = "TransportError";
126
+ }
127
+ };
128
+ var ConfigurationError = class extends SafeguardError {
129
+ constructor(message, options) {
130
+ super(message, options);
131
+ this.name = "ConfigurationError";
132
+ }
133
+ };
134
+ //#endregion
135
+ //#region src/secret.ts
136
+ const REDACTED = "[REDACTED]";
137
+ /**
138
+ * A wrapper that prevents accidental logging of sensitive values.
139
+ *
140
+ * - `toString()` returns "[REDACTED]"
141
+ * - `toJSON()` returns "[REDACTED]"
142
+ * - `console.log(secretValue)` prints "[REDACTED]"
143
+ * - `.expose()` returns the raw value when intentionally needed
144
+ */
145
+ var SecretValue = class {
146
+ #value;
147
+ constructor(value) {
148
+ this.#value = value;
149
+ }
150
+ /** Returns the raw secret. Use only when you intentionally need the plaintext. */
151
+ expose() {
152
+ return this.#value;
153
+ }
154
+ toString() {
155
+ return REDACTED;
156
+ }
157
+ toJSON() {
158
+ return REDACTED;
159
+ }
160
+ /** Node.js custom inspect (prevents leaking in console.log / util.inspect). */
161
+ [Symbol.for("nodejs.util.inspect.custom")]() {
162
+ return REDACTED;
163
+ }
164
+ get [Symbol.toStringTag]() {
165
+ return "SecretValue";
166
+ }
167
+ };
168
+ //#endregion
169
+ //#region src/utils.ts
170
+ /**
171
+ * Validates that a host string is a bare hostname or IP address.
172
+ * Rejects URLs, paths, query strings, userinfo, and embedded ports.
173
+ *
174
+ * @throws {ConfigurationError} if the host contains disallowed characters
175
+ */
176
+ function validateHost(host) {
177
+ if (!host) throw new ConfigurationError("Host must not be empty");
178
+ if (host.trim() !== host) throw new ConfigurationError("Host must not contain leading or trailing whitespace");
179
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(host)) throw new ConfigurationError(`Host must be a bare hostname or IP, not a URL. Remove the protocol prefix (e.g., use "${host.replace(/^[a-z][a-z0-9+.-]*:\/\//i, "")}")`);
180
+ if (/[/?#@]/.test(host)) throw new ConfigurationError("Host must be a bare hostname or IP address — must not contain /, ?, #, or @");
181
+ if (/:\d+$/.test(host) && !host.startsWith("[")) throw new ConfigurationError("Host must not include a port number. The SDK always connects over HTTPS (port 443).");
182
+ return host;
183
+ }
184
+ /**
185
+ * Builds the base URL for a Safeguard service endpoint.
186
+ *
187
+ * @example
188
+ * buildServiceUrl('appliance.example.com', Service.CORE)
189
+ * // => 'https://appliance.example.com/service/core'
190
+ */
191
+ function buildServiceUrl(host, service) {
192
+ if (service === "RSTS") return `https://${host}/RSTS`;
193
+ return `https://${host}/service/${service}`;
194
+ }
195
+ /**
196
+ * Builds a full API URL including path and optional query parameters.
197
+ *
198
+ * @example
199
+ * buildRequestUrl('appliance.example.com', Service.CORE, 'v4/Users', { filter: 'Name eq "admin"' })
200
+ * // => 'https://appliance.example.com/service/core/v4/Users?filter=Name+eq+%22admin%22'
201
+ */
202
+ function buildRequestUrl(host, service, relativeUrl, query) {
203
+ const base = buildServiceUrl(host, service);
204
+ const path = relativeUrl.startsWith("/") ? relativeUrl.slice(1) : relativeUrl;
205
+ const url = new URL(`${base}/${path}`);
206
+ if (query) for (const [key, value] of Object.entries(query)) url.searchParams.set(key, String(value));
207
+ return url.toString();
208
+ }
209
+ /**
210
+ * Encodes bytes to a URL-safe base64 string (no padding).
211
+ */
212
+ function base64UrlEncode(bytes) {
213
+ const binary = String.fromCharCode(...bytes);
214
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
215
+ }
216
+ /**
217
+ * Generates a cryptographically random code verifier for PKCE.
218
+ * Returns a 43-character base64url string (32 random bytes).
219
+ */
220
+ function generateCodeVerifier() {
221
+ const bytes = new Uint8Array(32);
222
+ crypto.getRandomValues(bytes);
223
+ return base64UrlEncode(bytes);
224
+ }
225
+ /**
226
+ * Computes the S256 code challenge from a code verifier.
227
+ * Uses Web Crypto API (available in Node 20+ and all modern browsers).
228
+ */
229
+ async function generateCodeChallenge(verifier) {
230
+ const data = new TextEncoder().encode(verifier);
231
+ const digest = await crypto.subtle.digest("SHA-256", data);
232
+ return base64UrlEncode(new Uint8Array(digest));
233
+ }
234
+ /**
235
+ * Generates a random state parameter for OAuth flows.
236
+ */
237
+ function generateState() {
238
+ const bytes = new Uint8Array(16);
239
+ crypto.getRandomValues(bytes);
240
+ return base64UrlEncode(bytes);
241
+ }
242
+ /**
243
+ * Extracts the `exp` claim from a JWT and returns expiresIn (seconds from now).
244
+ * Returns undefined if the token is not a valid JWT or has no `exp` claim.
245
+ */
246
+ function getTokenExpiresIn(jwt) {
247
+ try {
248
+ const parts = jwt.split(".");
249
+ if (parts.length !== 3) return void 0;
250
+ const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
251
+ const json = atob(payload);
252
+ const claims = JSON.parse(json);
253
+ if (typeof claims.exp !== "number") return void 0;
254
+ const now = Math.floor(Date.now() / 1e3);
255
+ return Math.max(0, claims.exp - now);
256
+ } catch {
257
+ return;
258
+ }
259
+ }
260
+ //#endregion
261
+ //#region src/storage/memory.ts
262
+ /**
263
+ * In-memory storage provider. Default for Node.js environments.
264
+ * Data does not persist across process restarts.
265
+ */
266
+ var MemoryStorage = class {
267
+ #store = /* @__PURE__ */ new Map();
268
+ get(key) {
269
+ return this.#store.get(key) ?? null;
270
+ }
271
+ set(key, value) {
272
+ this.#store.set(key, value);
273
+ }
274
+ remove(key) {
275
+ this.#store.delete(key);
276
+ }
277
+ clear() {
278
+ this.#store.clear();
279
+ }
280
+ };
281
+ //#endregion
282
+ //#region src/storage/session.ts
283
+ /**
284
+ * Browser sessionStorage adapter. Data persists for the browser tab lifetime.
285
+ * Requires a DOM environment (browser or jsdom).
286
+ */
287
+ var BrowserSessionStorage = class {
288
+ get(key) {
289
+ return sessionStorage.getItem(key);
290
+ }
291
+ set(key, value) {
292
+ sessionStorage.setItem(key, value);
293
+ }
294
+ remove(key) {
295
+ sessionStorage.removeItem(key);
296
+ }
297
+ clear() {
298
+ sessionStorage.clear();
299
+ }
300
+ };
301
+ //#endregion
302
+ //#region src/storage/types.ts
303
+ /** Well-known storage keys used by the SDK. */
304
+ const StorageKeys = {
305
+ STATE: "safeguard_state",
306
+ CODE_VERIFIER: "safeguard_code_verifier"
307
+ };
308
+ //#endregion
309
+ //#region src/http/node.ts
310
+ /**
311
+ * Node.js HTTP client using undici for full TLS control.
312
+ * Creates a per-instance Agent — no global state mutation.
313
+ */
314
+ var NodeHttpClient = class {
315
+ #agent;
316
+ constructor(tlsOptions) {
317
+ this.#agent = new undici.Agent({ connect: {
318
+ ca: tlsOptions?.ca,
319
+ cert: tlsOptions?.cert,
320
+ key: tlsOptions?.key,
321
+ passphrase: tlsOptions?.passphrase,
322
+ rejectUnauthorized: tlsOptions?.rejectUnauthorized ?? true
323
+ } });
324
+ }
325
+ async request(options) {
326
+ const { url, method, headers, body, signal, timeout } = options;
327
+ const requestOptions = {
328
+ method,
329
+ dispatcher: this.#agent
330
+ };
331
+ if (headers) requestOptions.headers = headers;
332
+ if (body != null) requestOptions.body = body;
333
+ if (signal) requestOptions.signal = signal;
334
+ if (timeout) {
335
+ requestOptions.bodyTimeout = timeout;
336
+ requestOptions.headersTimeout = timeout;
337
+ }
338
+ const response = await (0, undici.request)(url, requestOptions);
339
+ const responseHeaders = {};
340
+ const rawHeaders = response.headers;
341
+ for (const [key, value] of Object.entries(rawHeaders)) if (value !== void 0) responseHeaders[key] = Array.isArray(value) ? value.join(", ") : value;
342
+ const responseBody = await response.body.text();
343
+ return {
344
+ status: response.statusCode,
345
+ headers: responseHeaders,
346
+ body: responseBody
347
+ };
348
+ }
349
+ dispose() {
350
+ this.#agent.close().catch(() => {});
351
+ }
352
+ };
353
+ //#endregion
354
+ //#region src/http/browser.ts
355
+ /**
356
+ * Browser HTTP client using native fetch API.
357
+ * No TLS configuration needed — browser handles certificates.
358
+ */
359
+ var BrowserHttpClient = class {
360
+ #credentials;
361
+ constructor(options) {
362
+ this.#credentials = options?.credentials ?? "include";
363
+ }
364
+ async request(options) {
365
+ const { url, method, headers, body, signal, timeout } = options;
366
+ const requestInit = {
367
+ method,
368
+ credentials: this.#credentials
369
+ };
370
+ if (headers) requestInit.headers = headers;
371
+ if (body != null) requestInit.body = body;
372
+ if (signal || timeout) {
373
+ const controller = new AbortController();
374
+ let timeoutId;
375
+ if (timeout) timeoutId = setTimeout(() => controller.abort(/* @__PURE__ */ new Error("Request timed out")), timeout);
376
+ requestInit.signal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
377
+ try {
378
+ return await this.#doFetch(url, requestInit);
379
+ } finally {
380
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
381
+ }
382
+ }
383
+ return this.#doFetch(url, requestInit);
384
+ }
385
+ async #doFetch(url, init) {
386
+ const response = await fetch(url, init);
387
+ const responseHeaders = {};
388
+ response.headers.forEach((value, key) => {
389
+ responseHeaders[key] = value;
390
+ });
391
+ const responseBody = await response.text();
392
+ return {
393
+ status: response.status,
394
+ headers: responseHeaders,
395
+ body: responseBody
396
+ };
397
+ }
398
+ };
399
+ //#endregion
400
+ //#region src/auth/password.ts
401
+ /**
402
+ * Authenticates using username/password via RSTS Resource Owner grant.
403
+ */
404
+ var PasswordAuth = class {
405
+ #username;
406
+ #password;
407
+ #provider;
408
+ constructor(options) {
409
+ if (!options.username) throw new ConfigurationError("PasswordAuth requires username");
410
+ if (!options.password) throw new ConfigurationError("PasswordAuth requires password");
411
+ if (!options.provider) throw new ConfigurationError("PasswordAuth requires provider");
412
+ this.#username = options.username;
413
+ this.#password = new SecretValue(options.password);
414
+ this.#provider = options.provider;
415
+ }
416
+ get description() {
417
+ return `PasswordAuth(${this.#username}@${this.#provider})`;
418
+ }
419
+ async authenticate(host, httpClient, _storage) {
420
+ const rstsToken = await this.#getRstsToken(host, httpClient);
421
+ return await this.#exchangeForUserToken(host, httpClient, rstsToken);
422
+ }
423
+ async refreshToken(host, httpClient, storage) {
424
+ return this.authenticate(host, httpClient, storage);
425
+ }
426
+ async #getRstsToken(host, httpClient) {
427
+ const body = JSON.stringify({
428
+ grant_type: "password",
429
+ username: this.#username,
430
+ password: this.#password.expose(),
431
+ scope: "rsts:sts:primaryproviderid:" + this.#provider.toLowerCase()
432
+ });
433
+ const response = await httpClient.request({
434
+ url: `https://${host}/RSTS/oauth2/token`,
435
+ method: "POST",
436
+ headers: { "Content-Type": "application/json" },
437
+ body
438
+ });
439
+ if (response.status !== 200) {
440
+ const { ApiError } = await Promise.resolve().then(() => errors_exports);
441
+ throw ApiError.fromResponse(response.status, response.body);
442
+ }
443
+ return JSON.parse(response.body).access_token;
444
+ }
445
+ async #exchangeForUserToken(host, httpClient, rstsToken) {
446
+ const response = await httpClient.request({
447
+ url: `https://${host}/service/core/v4/Token/LoginResponse`,
448
+ method: "POST",
449
+ headers: {
450
+ "Content-Type": "application/json",
451
+ Authorization: `Bearer ${rstsToken}`
452
+ },
453
+ body: JSON.stringify({ StsAccessToken: rstsToken })
454
+ });
455
+ if (response.status !== 200) {
456
+ const { ApiError } = await Promise.resolve().then(() => errors_exports);
457
+ throw ApiError.fromResponse(response.status, response.body);
458
+ }
459
+ const data = JSON.parse(response.body);
460
+ const tokenSet = {
461
+ accessToken: new SecretValue(data.UserToken),
462
+ acquiredAt: Date.now()
463
+ };
464
+ if (data.ExpiresIn != null) tokenSet.expiresIn = data.ExpiresIn;
465
+ else {
466
+ const expiresIn = getTokenExpiresIn(data.UserToken);
467
+ if (expiresIn != null) tokenSet.expiresIn = expiresIn;
468
+ }
469
+ return tokenSet;
470
+ }
471
+ };
472
+ //#endregion
473
+ //#region src/auth/certificate.ts
474
+ /**
475
+ * Authenticates using a client certificate (mTLS) against RSTS.
476
+ * Node.js only — requires TLS client cert support in HttpClient.
477
+ */
478
+ var CertificateAuth = class {
479
+ #certFile;
480
+ #keyFile;
481
+ #cert;
482
+ #key;
483
+ #pfxFile;
484
+ #pfx;
485
+ #passphrase;
486
+ #provider;
487
+ constructor(options) {
488
+ const hasPfx = options.pfx ?? options.pfxFile;
489
+ const hasCert = options.cert ?? options.certFile;
490
+ const hasKey = options.key ?? options.keyFile;
491
+ if (!hasPfx && !hasCert) throw new ConfigurationError("CertificateAuth requires cert/certFile or pfxFile/pfx");
492
+ if (!hasPfx && !hasKey) throw new ConfigurationError("CertificateAuth requires key or keyFile");
493
+ this.#certFile = options.certFile;
494
+ this.#keyFile = options.keyFile;
495
+ this.#cert = options.cert;
496
+ this.#key = options.key;
497
+ this.#pfxFile = options.pfxFile;
498
+ this.#pfx = options.pfx;
499
+ this.#passphrase = options.passphrase ? new SecretValue(options.passphrase) : void 0;
500
+ this.#provider = options.provider ?? "certificate";
501
+ }
502
+ get description() {
503
+ return `CertificateAuth(${this.#provider})`;
504
+ }
505
+ /** Returns the TLS options needed to create an mTLS HttpClient. */
506
+ getTlsOptions() {
507
+ const opts = {};
508
+ if (this.#pfx ?? this.#pfxFile) opts.pfx = this.#pfx ?? this.#pfxFile;
509
+ else {
510
+ opts.cert = this.#cert ?? this.#certFile;
511
+ opts.key = this.#key ?? this.#keyFile;
512
+ }
513
+ if (this.#passphrase) opts.passphrase = this.#passphrase.expose();
514
+ return opts;
515
+ }
516
+ async authenticate(host, httpClient, _storage) {
517
+ const rstsToken = await this.#getRstsToken(host, httpClient);
518
+ return this.#exchangeForUserToken(host, httpClient, rstsToken);
519
+ }
520
+ async refreshToken(host, httpClient, storage) {
521
+ return this.authenticate(host, httpClient, storage);
522
+ }
523
+ async #getRstsToken(host, httpClient) {
524
+ const body = JSON.stringify({
525
+ grant_type: "client_credentials",
526
+ scope: "rsts:sts:primaryproviderid:" + this.#provider.toLowerCase()
527
+ });
528
+ const response = await httpClient.request({
529
+ url: `https://${host}/RSTS/oauth2/token`,
530
+ method: "POST",
531
+ headers: { "Content-Type": "application/json" },
532
+ body
533
+ });
534
+ if (response.status !== 200) {
535
+ const { ApiError } = await Promise.resolve().then(() => errors_exports);
536
+ throw ApiError.fromResponse(response.status, response.body);
537
+ }
538
+ return JSON.parse(response.body).access_token;
539
+ }
540
+ async #exchangeForUserToken(host, httpClient, rstsToken) {
541
+ const response = await httpClient.request({
542
+ url: `https://${host}/service/core/v4/Token/LoginResponse`,
543
+ method: "POST",
544
+ headers: {
545
+ "Content-Type": "application/json",
546
+ Authorization: `Bearer ${rstsToken}`
547
+ },
548
+ body: JSON.stringify({ StsAccessToken: rstsToken })
549
+ });
550
+ if (response.status !== 200) {
551
+ const { ApiError } = await Promise.resolve().then(() => errors_exports);
552
+ throw ApiError.fromResponse(response.status, response.body);
553
+ }
554
+ const data = JSON.parse(response.body);
555
+ const tokenSet = {
556
+ accessToken: new SecretValue(data.UserToken),
557
+ acquiredAt: Date.now()
558
+ };
559
+ if (data.ExpiresIn != null) tokenSet.expiresIn = data.ExpiresIn;
560
+ else {
561
+ const expiresIn = getTokenExpiresIn(data.UserToken);
562
+ if (expiresIn != null) tokenSet.expiresIn = expiresIn;
563
+ }
564
+ return tokenSet;
565
+ }
566
+ };
567
+ //#endregion
568
+ //#region src/auth/pkce.ts
569
+ /**
570
+ * Authenticates using PKCE (Proof Key for Code Exchange) with browser redirect.
571
+ * For browser environments where the user is redirected to RSTS login.
572
+ */
573
+ var PkceAuth = class {
574
+ #redirectUri;
575
+ #provider;
576
+ constructor(options) {
577
+ if (!options.redirectUri) throw new ConfigurationError("PkceAuth requires redirectUri");
578
+ if (!options.provider) throw new ConfigurationError("PkceAuth requires provider");
579
+ this.#redirectUri = options.redirectUri;
580
+ this.#provider = options.provider;
581
+ }
582
+ get description() {
583
+ return `PKCE(redirect=${this.#redirectUri})`;
584
+ }
585
+ /**
586
+ * Initiates the PKCE flow. If a stored authorization code exists (from callback),
587
+ * exchanges it for a token. Otherwise, redirects to RSTS login.
588
+ */
589
+ async authenticate(host, httpClient, storage) {
590
+ const code = storage.get("safeguard_auth_code");
591
+ if (code) {
592
+ const verifier = storage.get(StorageKeys.CODE_VERIFIER);
593
+ if (!verifier) throw new ConfigurationError("PKCE code verifier not found in storage");
594
+ storage.remove("safeguard_auth_code");
595
+ storage.remove(StorageKeys.CODE_VERIFIER);
596
+ storage.remove(StorageKeys.STATE);
597
+ return this.#exchangeCode(host, httpClient, code, verifier);
598
+ }
599
+ await this.#initiateRedirect(host, storage);
600
+ throw new ConfigurationError("PKCE redirect initiated. Call handlePkceCallback() on the callback page.");
601
+ }
602
+ async #initiateRedirect(host, storage) {
603
+ const verifier = generateCodeVerifier();
604
+ const challenge = await generateCodeChallenge(verifier);
605
+ const state = generateState();
606
+ storage.set(StorageKeys.CODE_VERIFIER, verifier);
607
+ storage.set(StorageKeys.STATE, state);
608
+ const authUrl = `https://${host}/RSTS/Login?${new URLSearchParams({
609
+ response_type: "code",
610
+ client_id: "safeguard.js",
611
+ redirect_uri: this.#redirectUri,
612
+ scope: "rsts:sts:primaryproviderid:" + this.#provider,
613
+ code_challenge: challenge,
614
+ code_challenge_method: "S256",
615
+ state
616
+ }).toString()}`;
617
+ if (typeof window !== "undefined" && window.location) window.location.href = authUrl;
618
+ }
619
+ async #exchangeCode(host, httpClient, code, verifier) {
620
+ const body = new URLSearchParams({
621
+ grant_type: "authorization_code",
622
+ code,
623
+ redirect_uri: this.#redirectUri,
624
+ client_id: "safeguard.js",
625
+ code_verifier: verifier
626
+ });
627
+ const response = await httpClient.request({
628
+ url: `https://${host}/RSTS/oauth2/token`,
629
+ method: "POST",
630
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
631
+ body: body.toString()
632
+ });
633
+ if (response.status !== 200) {
634
+ const { ApiError } = await Promise.resolve().then(() => errors_exports);
635
+ throw ApiError.fromResponse(response.status, response.body);
636
+ }
637
+ const data = JSON.parse(response.body);
638
+ const userTokenResponse = await httpClient.request({
639
+ url: `https://${host}/service/core/v4/Token/LoginResponse`,
640
+ method: "POST",
641
+ headers: {
642
+ "Content-Type": "application/json",
643
+ Authorization: `Bearer ${data.access_token}`
644
+ },
645
+ body: JSON.stringify({ StsAccessToken: data.access_token })
646
+ });
647
+ if (userTokenResponse.status !== 200) {
648
+ const { ApiError } = await Promise.resolve().then(() => errors_exports);
649
+ throw ApiError.fromResponse(userTokenResponse.status, userTokenResponse.body);
650
+ }
651
+ const userData = JSON.parse(userTokenResponse.body);
652
+ const tokenSet = {
653
+ accessToken: new SecretValue(userData.UserToken),
654
+ acquiredAt: Date.now()
655
+ };
656
+ if (userData.ExpiresIn != null) tokenSet.expiresIn = userData.ExpiresIn;
657
+ return tokenSet;
658
+ }
659
+ };
660
+ //#endregion
661
+ //#region src/auth/pkce-noninteractive.ts
662
+ /**
663
+ * Non-interactive PKCE authentication for Node.js automation.
664
+ * Starts a local HTTP server to receive the callback, opens the system browser
665
+ * for login, and waits for the redirect.
666
+ *
667
+ * Matches PySafeguard's PkceAuth behavior (local server pattern).
668
+ */
669
+ var PkceNonInteractiveAuth = class {
670
+ #provider;
671
+ #callbackPort;
672
+ constructor(options) {
673
+ this.#provider = options?.provider ?? "local";
674
+ this.#callbackPort = options?.callbackPort ?? 8400;
675
+ }
676
+ get description() {
677
+ return `PkceNonInteractiveAuth(port=${String(this.#callbackPort)})`;
678
+ }
679
+ async authenticate(host, httpClient, _storage) {
680
+ const { createServer } = await import("node:http");
681
+ const { once } = await import("node:events");
682
+ const verifier = generateCodeVerifier();
683
+ const challenge = await generateCodeChallenge(verifier);
684
+ const state = generateState();
685
+ const redirectUri = `http://localhost:${String(this.#callbackPort)}/callback`;
686
+ const server = createServer();
687
+ server.listen(this.#callbackPort);
688
+ const authUrl = `https://${host}/RSTS/Login?${new URLSearchParams({
689
+ response_type: "code",
690
+ client_id: "safeguard.js",
691
+ redirect_uri: redirectUri,
692
+ scope: "rsts:sts:primaryproviderid:" + this.#provider,
693
+ code_challenge: challenge,
694
+ code_challenge_method: "S256",
695
+ state
696
+ }).toString()}`;
697
+ await this.#openBrowser(authUrl);
698
+ const code = await new Promise((resolve, reject) => {
699
+ const timeout = setTimeout(() => {
700
+ server.close();
701
+ reject(new ConfigurationError("PKCE callback timed out after 120 seconds"));
702
+ }, 12e4);
703
+ server.on("request", (req, res) => {
704
+ const url = new URL(req.url ?? "/", `http://localhost:${String(this.#callbackPort)}`);
705
+ if (url.pathname === "/callback") {
706
+ const receivedCode = url.searchParams.get("code");
707
+ if (url.searchParams.get("state") !== state) {
708
+ res.writeHead(400, { "Content-Type": "text/plain" });
709
+ res.end("State mismatch");
710
+ clearTimeout(timeout);
711
+ server.close();
712
+ reject(new ConfigurationError("PKCE state mismatch"));
713
+ return;
714
+ }
715
+ if (!receivedCode) {
716
+ res.writeHead(400, { "Content-Type": "text/plain" });
717
+ res.end("No authorization code received");
718
+ clearTimeout(timeout);
719
+ server.close();
720
+ reject(new ConfigurationError("No authorization code in callback"));
721
+ return;
722
+ }
723
+ res.writeHead(200, { "Content-Type": "text/html" });
724
+ res.end("<html><body><h1>Authentication successful!</h1><p>You may close this window.</p></body></html>");
725
+ clearTimeout(timeout);
726
+ server.close();
727
+ resolve(receivedCode);
728
+ }
729
+ });
730
+ once(server, "error").then(([err]) => {
731
+ clearTimeout(timeout);
732
+ reject(err);
733
+ }).catch(reject);
734
+ });
735
+ return this.#exchangeCode(host, httpClient, code, verifier, redirectUri);
736
+ }
737
+ async #exchangeCode(host, httpClient, code, verifier, redirectUri) {
738
+ const body = new URLSearchParams({
739
+ grant_type: "authorization_code",
740
+ code,
741
+ redirect_uri: redirectUri,
742
+ client_id: "safeguard.js",
743
+ code_verifier: verifier
744
+ });
745
+ const response = await httpClient.request({
746
+ url: `https://${host}/RSTS/oauth2/token`,
747
+ method: "POST",
748
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
749
+ body: body.toString()
750
+ });
751
+ if (response.status !== 200) {
752
+ const { ApiError } = await Promise.resolve().then(() => errors_exports);
753
+ throw ApiError.fromResponse(response.status, response.body);
754
+ }
755
+ const data = JSON.parse(response.body);
756
+ const userResponse = await httpClient.request({
757
+ url: `https://${host}/service/core/v4/Token/LoginResponse`,
758
+ method: "POST",
759
+ headers: {
760
+ "Content-Type": "application/json",
761
+ Authorization: `Bearer ${data.access_token}`
762
+ },
763
+ body: JSON.stringify({ StsAccessToken: data.access_token })
764
+ });
765
+ if (userResponse.status !== 200) {
766
+ const { ApiError } = await Promise.resolve().then(() => errors_exports);
767
+ throw ApiError.fromResponse(userResponse.status, userResponse.body);
768
+ }
769
+ const userData = JSON.parse(userResponse.body);
770
+ const tokenSet = {
771
+ accessToken: new SecretValue(userData.UserToken),
772
+ acquiredAt: Date.now()
773
+ };
774
+ if (userData.ExpiresIn != null) tokenSet.expiresIn = userData.ExpiresIn;
775
+ return tokenSet;
776
+ }
777
+ async #openBrowser(url) {
778
+ const { platform } = await import("node:os");
779
+ const { execFile } = await import("node:child_process");
780
+ if (platform() === "win32") execFile("cmd", [
781
+ "/c",
782
+ "start",
783
+ "",
784
+ url
785
+ ]);
786
+ else if (platform() === "darwin") execFile("open", [url]);
787
+ else execFile("xdg-open", [url]);
788
+ }
789
+ };
790
+ //#endregion
791
+ //#region src/auth/pkce-callback.ts
792
+ /**
793
+ * Parses the PKCE callback URL and stores the authorization code.
794
+ * Call this on the callback/redirect page to complete the PKCE flow.
795
+ *
796
+ * This is the explicit replacement for the legacy `safeguardJsAccessTokenChecker.js`
797
+ * script that ran as a side-effect on import.
798
+ *
799
+ * @param storage - StorageProvider to persist the code
800
+ * @param url - The callback URL to parse (defaults to window.location.href)
801
+ * @returns The parsed code and state, or null if not a valid callback
802
+ */
803
+ function handlePkceCallback(storage, url) {
804
+ const callbackUrl = url ?? (typeof window !== "undefined" ? window.location.href : "");
805
+ if (!callbackUrl) return null;
806
+ const parsed = new URL(callbackUrl);
807
+ const code = parsed.searchParams.get("code");
808
+ const state = parsed.searchParams.get("state");
809
+ if (!code || !state) return null;
810
+ const storedState = storage.get(StorageKeys.STATE);
811
+ if (storedState && storedState !== state) throw new ConfigurationError("PKCE state mismatch — possible CSRF attack");
812
+ storage.set("safeguard_auth_code", code);
813
+ return {
814
+ code,
815
+ state
816
+ };
817
+ }
818
+ //#endregion
819
+ //#region src/auth/token.ts
820
+ /**
821
+ * Uses a pre-existing access token. No authentication flow — the token is assumed valid.
822
+ * Useful when tokens are obtained externally or for service-to-service scenarios.
823
+ */
824
+ var TokenAuth = class {
825
+ #accessToken;
826
+ constructor(options) {
827
+ if (!options.accessToken) throw new ConfigurationError("TokenAuth requires accessToken");
828
+ this.#accessToken = new SecretValue(options.accessToken);
829
+ }
830
+ get description() {
831
+ return "TokenAuth(pre-existing)";
832
+ }
833
+ async authenticate(_host, _httpClient, _storage) {
834
+ return {
835
+ accessToken: this.#accessToken,
836
+ acquiredAt: Date.now()
837
+ };
838
+ }
839
+ };
840
+ //#endregion
841
+ //#region src/auth/anonymous.ts
842
+ /**
843
+ * Anonymous authentication — no credentials.
844
+ * Used for accessing public Safeguard endpoints (e.g., appliance status).
845
+ */
846
+ var AnonymousAuth = class {
847
+ get description() {
848
+ return "AnonymousAuth";
849
+ }
850
+ async authenticate(_host, _httpClient, _storage) {
851
+ return {
852
+ accessToken: new SecretValue(""),
853
+ acquiredAt: Date.now()
854
+ };
855
+ }
856
+ };
857
+ //#endregion
858
+ //#region src/client.ts
859
+ const DEFAULT_TIMEOUT = 3e5;
860
+ const DEFAULT_API_VERSION = "v4";
861
+ const TOKEN_REFRESH_MARGIN_MS = 6e4;
862
+ /**
863
+ * Main entry point for the Safeguard SDK.
864
+ * Instance-based — no global state. Multiple clients to different appliances are supported.
865
+ */
866
+ var SafeguardClient = class {
867
+ #host;
868
+ #auth;
869
+ #apiVersion;
870
+ #timeout;
871
+ #autoRefresh;
872
+ #ca;
873
+ #verify;
874
+ #httpClient;
875
+ #storage;
876
+ #tokenSet;
877
+ #connected = false;
878
+ constructor(host, options) {
879
+ if (!host) throw new ConfigurationError("SafeguardClient requires a host");
880
+ if (!options.auth) throw new ConfigurationError("SafeguardClient requires an auth strategy");
881
+ this.#host = validateHost(host);
882
+ this.#auth = options.auth;
883
+ this.#apiVersion = options.apiVersion ?? DEFAULT_API_VERSION;
884
+ this.#timeout = options.timeout ?? DEFAULT_TIMEOUT;
885
+ this.#autoRefresh = options.autoRefresh !== false;
886
+ this.#ca = options.ca;
887
+ this.#verify = options.verify !== false;
888
+ this.#storage = new MemoryStorage();
889
+ }
890
+ /** The appliance hostname this client connects to. */
891
+ get host() {
892
+ return this.#host;
893
+ }
894
+ /** The API version prefix (e.g. 'v4'). */
895
+ get apiVersion() {
896
+ return this.#apiVersion;
897
+ }
898
+ /** Custom CA certificate, if provided. */
899
+ get ca() {
900
+ return this.#ca;
901
+ }
902
+ /** Whether TLS verification is enabled. */
903
+ get verify() {
904
+ return this.#verify;
905
+ }
906
+ /** Whether the client has an active authenticated session. */
907
+ get isConnected() {
908
+ return this.#connected;
909
+ }
910
+ /**
911
+ * Sets a custom StorageProvider (e.g., BrowserSessionStorage).
912
+ * Must be called before connect().
913
+ */
914
+ setStorage(storage) {
915
+ this.#storage = storage;
916
+ return this;
917
+ }
918
+ /**
919
+ * Sets the HttpClient implementation. If not called, must be set before connect().
920
+ * The factory pattern allows platform-specific client injection:
921
+ * - Node: `new NodeHttpClient({ ca, rejectUnauthorized })`
922
+ * - Browser: `new BrowserHttpClient()`
923
+ */
924
+ setHttpClient(httpClient) {
925
+ this.#httpClient = httpClient;
926
+ return this;
927
+ }
928
+ /**
929
+ * Authenticate and establish a session.
930
+ * @throws {AuthenticationError} if authentication fails
931
+ * @throws {ConfigurationError} if httpClient not set
932
+ */
933
+ async connect() {
934
+ if (!this.#httpClient) throw new ConfigurationError("HttpClient not set. Call setHttpClient() before connect(), or use SafeguardClient.createNode() / SafeguardClient.createBrowser().");
935
+ this.#tokenSet = await this.#auth.authenticate(this.#host, this.#httpClient, this.#storage);
936
+ if (!this.#tokenSet.accessToken.expose()) throw new AuthenticationError("Authentication returned empty access token");
937
+ this.#connected = true;
938
+ }
939
+ /**
940
+ * Disconnect — dispose HTTP client and clear token.
941
+ */
942
+ async disconnect() {
943
+ this.#tokenSet = void 0;
944
+ this.#connected = false;
945
+ this.#httpClient?.dispose?.();
946
+ }
947
+ /**
948
+ * Returns seconds until the current access token expires, or -1 if unknown.
949
+ */
950
+ getAccessTokenLifetimeRemaining() {
951
+ if (!this.#tokenSet?.expiresIn) return -1;
952
+ const elapsed = (Date.now() - this.#tokenSet.acquiredAt) / 1e3;
953
+ return Math.max(0, this.#tokenSet.expiresIn - elapsed);
954
+ }
955
+ async get(service, relativeUrl, options) {
956
+ return this.invoke(service, "GET", relativeUrl, options);
957
+ }
958
+ async post(service, relativeUrl, options) {
959
+ return this.invoke(service, "POST", relativeUrl, options);
960
+ }
961
+ async put(service, relativeUrl, options) {
962
+ return this.invoke(service, "PUT", relativeUrl, options);
963
+ }
964
+ async delete(service, relativeUrl, options) {
965
+ return this.invoke(service, "DELETE", relativeUrl, options);
966
+ }
967
+ async patch(service, relativeUrl, options) {
968
+ return this.invoke(service, "PATCH", relativeUrl, options);
969
+ }
970
+ /**
971
+ * Low-level request method. Convenience verbs delegate here.
972
+ *
973
+ * When `options.fullResponse` is true, returns SafeguardResponse<T> with status + headers.
974
+ * Otherwise returns just the parsed response body as T.
975
+ */
976
+ async invoke(service, method, relativeUrl, options) {
977
+ if (!this.#connected || !this.#httpClient) throw new ConfigurationError("Client not connected. Call connect() first.");
978
+ if (this.#autoRefresh) await this.#ensureValidToken();
979
+ const url = buildRequestUrl(this.#host, service, relativeUrl, options?.query);
980
+ const headers = {
981
+ Authorization: `Bearer ${this.#tokenSet.accessToken.expose()}`,
982
+ Accept: "application/json",
983
+ ...options?.headers
984
+ };
985
+ let body = null;
986
+ if (options?.json != null) {
987
+ body = JSON.stringify(options.json);
988
+ headers["Content-Type"] = "application/json";
989
+ } else if (options?.body != null) body = options.body;
990
+ const requestOpts = {
991
+ url,
992
+ method,
993
+ headers,
994
+ body,
995
+ timeout: options?.timeout ?? this.#timeout
996
+ };
997
+ if (options?.signal) requestOpts.signal = options.signal;
998
+ const response = await this.#httpClient.request(requestOpts);
999
+ if (response.status >= 400) throw ApiError.fromResponse(response.status, response.body);
1000
+ const data = response.body ? JSON.parse(response.body) : void 0;
1001
+ if (options?.fullResponse) return {
1002
+ data,
1003
+ status: response.status,
1004
+ headers: response.headers
1005
+ };
1006
+ return data;
1007
+ }
1008
+ async #ensureValidToken() {
1009
+ if (!this.#tokenSet) return;
1010
+ if (!this.#tokenSet.expiresIn) return;
1011
+ const elapsed = Date.now() - this.#tokenSet.acquiredAt;
1012
+ const expiresInMs = this.#tokenSet.expiresIn * 1e3;
1013
+ if (elapsed + TOKEN_REFRESH_MARGIN_MS >= expiresInMs) {
1014
+ if (this.#auth.refreshToken) {
1015
+ const refreshed = await this.#auth.refreshToken(this.#host, this.#httpClient, this.#storage);
1016
+ if (refreshed) {
1017
+ this.#tokenSet = refreshed;
1018
+ return;
1019
+ }
1020
+ }
1021
+ this.#tokenSet = await this.#auth.authenticate(this.#host, this.#httpClient, this.#storage);
1022
+ }
1023
+ }
1024
+ };
1025
+ //#endregion
1026
+ //#region src/a2a/types.ts
1027
+ /**
1028
+ * SSH key formats for private key retrieval.
1029
+ */
1030
+ let SshKeyFormat = /* @__PURE__ */ function(SshKeyFormat) {
1031
+ SshKeyFormat["OpenSsh"] = "OpenSsh";
1032
+ SshKeyFormat["Ssh2"] = "Ssh2";
1033
+ SshKeyFormat["Putty"] = "Putty";
1034
+ return SshKeyFormat;
1035
+ }({});
1036
+ /**
1037
+ * Brokered access request types.
1038
+ */
1039
+ let BrokeredAccessRequestType = /* @__PURE__ */ function(BrokeredAccessRequestType) {
1040
+ BrokeredAccessRequestType["Password"] = "Password";
1041
+ BrokeredAccessRequestType["SshKey"] = "SshKey";
1042
+ BrokeredAccessRequestType["ApiKey"] = "ApiKey";
1043
+ BrokeredAccessRequestType["Token"] = "Token";
1044
+ BrokeredAccessRequestType["RdpFile"] = "RdpFile";
1045
+ BrokeredAccessRequestType["RemoteDesktop"] = "RemoteDesktop";
1046
+ BrokeredAccessRequestType["SshSession"] = "SshSession";
1047
+ return BrokeredAccessRequestType;
1048
+ }({});
1049
+ //#endregion
1050
+ //#region src/a2a/index.ts
1051
+ /**
1052
+ * Application-to-Application client for programmatic credential retrieval.
1053
+ * Uses client certificate authentication exclusively.
1054
+ *
1055
+ * Full parity with PySafeguard and SafeguardDotNet A2A clients.
1056
+ */
1057
+ var A2AClient = class {
1058
+ #host;
1059
+ #auth;
1060
+ #ca;
1061
+ #verify;
1062
+ #httpClient;
1063
+ constructor(host, options) {
1064
+ if (!host) throw new ConfigurationError("A2AClient requires a host");
1065
+ if (!options.auth) throw new ConfigurationError("A2AClient requires a CertificateAuth");
1066
+ this.#host = host;
1067
+ this.#auth = options.auth;
1068
+ this.#ca = options.ca;
1069
+ this.#verify = options.verify !== false;
1070
+ }
1071
+ /** The appliance hostname. */
1072
+ get host() {
1073
+ return this.#host;
1074
+ }
1075
+ /** Custom CA, if set. */
1076
+ get ca() {
1077
+ return this.#ca;
1078
+ }
1079
+ /** Whether TLS verification is enabled. */
1080
+ get verify() {
1081
+ return this.#verify;
1082
+ }
1083
+ /** The CertificateAuth providing TLS client cert configuration. */
1084
+ get auth() {
1085
+ return this.#auth;
1086
+ }
1087
+ /**
1088
+ * Sets the HttpClient (must be configured with client cert TLS).
1089
+ */
1090
+ setHttpClient(httpClient) {
1091
+ this.#httpClient = httpClient;
1092
+ return this;
1093
+ }
1094
+ /**
1095
+ * Retrieve a password credential via A2A.
1096
+ * @param apiKey - The API key for the retrievable account
1097
+ */
1098
+ async retrievePassword(apiKey) {
1099
+ return new SecretValue(await this.#a2aRequest(apiKey, "Credentials", "Password"));
1100
+ }
1101
+ /**
1102
+ * Retrieve an SSH private key credential via A2A.
1103
+ * @param apiKey - The API key for the retrievable account
1104
+ * @param format - SSH key format (default: OpenSsh)
1105
+ */
1106
+ async retrievePrivateKey(apiKey, format) {
1107
+ const keyType = format ?? "OpenSsh";
1108
+ return new SecretValue(await this.#a2aRequest(apiKey, "Credentials", `SshKey?keyFormat=${keyType}`));
1109
+ }
1110
+ /**
1111
+ * Retrieve an API key secret credential via A2A.
1112
+ * @param apiKey - The API key for the retrievable account
1113
+ */
1114
+ async retrieveApiKeySecret(apiKey) {
1115
+ return new SecretValue(await this.#a2aRequest(apiKey, "Credentials", "ApiKey"));
1116
+ }
1117
+ /**
1118
+ * Get the list of retrievable accounts available to this certificate.
1119
+ */
1120
+ async getRetrievableAccounts() {
1121
+ this.#ensureHttpClient();
1122
+ const response = await this.#httpClient.request({
1123
+ url: `https://${this.#host}/service/a2a/v4/A2ARegistrations`,
1124
+ method: "GET",
1125
+ headers: { Accept: "application/json" }
1126
+ });
1127
+ if (response.status !== 200) throw ApiError.fromResponse(response.status, response.body);
1128
+ return JSON.parse(response.body);
1129
+ }
1130
+ /**
1131
+ * Set a password on a managed account via A2A.
1132
+ * @param apiKey - The API key for the account
1133
+ * @param password - The new password value
1134
+ */
1135
+ async setPassword(apiKey, password) {
1136
+ this.#ensureHttpClient();
1137
+ const response = await this.#httpClient.request({
1138
+ url: `https://${this.#host}/service/a2a/v4/Credentials/Password`,
1139
+ method: "PUT",
1140
+ headers: {
1141
+ Authorization: `A2A ${apiKey}`,
1142
+ "Content-Type": "application/json",
1143
+ Accept: "application/json"
1144
+ },
1145
+ body: JSON.stringify(password)
1146
+ });
1147
+ if (response.status !== 200 && response.status !== 204) throw ApiError.fromResponse(response.status, response.body);
1148
+ }
1149
+ /**
1150
+ * Set a private key on a managed account via A2A.
1151
+ * @param apiKey - The API key for the account
1152
+ * @param key - The private key content
1153
+ * @param passphrase - Optional passphrase for the key
1154
+ * @param format - SSH key format (default: OpenSsh)
1155
+ */
1156
+ async setPrivateKey(apiKey, key, passphrase, format) {
1157
+ this.#ensureHttpClient();
1158
+ const payload = {
1159
+ PrivateKey: key,
1160
+ KeyFormat: format ?? "OpenSsh"
1161
+ };
1162
+ if (passphrase) payload["Passphrase"] = passphrase;
1163
+ const response = await this.#httpClient.request({
1164
+ url: `https://${this.#host}/service/a2a/v4/Credentials/SshKey`,
1165
+ method: "PUT",
1166
+ headers: {
1167
+ Authorization: `A2A ${apiKey}`,
1168
+ "Content-Type": "application/json",
1169
+ Accept: "application/json"
1170
+ },
1171
+ body: JSON.stringify(payload)
1172
+ });
1173
+ if (response.status !== 200 && response.status !== 204) throw ApiError.fromResponse(response.status, response.body);
1174
+ }
1175
+ /**
1176
+ * Submit a brokered access request via A2A.
1177
+ * @param apiKey - The API key for the access request
1178
+ * @param request - The access request details
1179
+ */
1180
+ async brokerAccessRequest(apiKey, request) {
1181
+ this.#ensureHttpClient();
1182
+ const response = await this.#httpClient.request({
1183
+ url: `https://${this.#host}/service/a2a/v4/AccessRequests`,
1184
+ method: "POST",
1185
+ headers: {
1186
+ Authorization: `A2A ${apiKey}`,
1187
+ "Content-Type": "application/json",
1188
+ Accept: "application/json"
1189
+ },
1190
+ body: JSON.stringify(request)
1191
+ });
1192
+ if (response.status !== 200 && response.status !== 201) throw ApiError.fromResponse(response.status, response.body);
1193
+ return JSON.parse(response.body);
1194
+ }
1195
+ async #a2aRequest(apiKey, ...pathSegments) {
1196
+ this.#ensureHttpClient();
1197
+ const path = pathSegments.join("/");
1198
+ const response = await this.#httpClient.request({
1199
+ url: `https://${this.#host}/service/a2a/v4/${path}`,
1200
+ method: "GET",
1201
+ headers: {
1202
+ Authorization: `A2A ${apiKey}`,
1203
+ Accept: "application/json"
1204
+ }
1205
+ });
1206
+ if (response.status !== 200) throw ApiError.fromResponse(response.status, response.body);
1207
+ return response.body;
1208
+ }
1209
+ #ensureHttpClient() {
1210
+ if (!this.#httpClient) throw new ConfigurationError("HttpClient not set. Call setHttpClient() with a client configured for mTLS.");
1211
+ }
1212
+ };
1213
+ //#endregion
1214
+ exports.A2AClient = A2AClient;
1215
+ exports.AnonymousAuth = AnonymousAuth;
1216
+ exports.ApiError = ApiError;
1217
+ exports.AuthenticationError = AuthenticationError;
1218
+ exports.AuthorizationError = AuthorizationError;
1219
+ exports.BrokeredAccessRequestType = BrokeredAccessRequestType;
1220
+ exports.BrowserHttpClient = BrowserHttpClient;
1221
+ exports.BrowserSessionStorage = BrowserSessionStorage;
1222
+ exports.CertificateAuth = CertificateAuth;
1223
+ exports.ConfigurationError = ConfigurationError;
1224
+ exports.HttpMethod = HttpMethod;
1225
+ exports.MemoryStorage = MemoryStorage;
1226
+ exports.NodeHttpClient = NodeHttpClient;
1227
+ exports.NotFoundError = NotFoundError;
1228
+ exports.PasswordAuth = PasswordAuth;
1229
+ exports.PkceAuth = PkceAuth;
1230
+ exports.PkceNonInteractiveAuth = PkceNonInteractiveAuth;
1231
+ exports.SafeguardClient = SafeguardClient;
1232
+ exports.SafeguardError = SafeguardError;
1233
+ exports.SecretValue = SecretValue;
1234
+ exports.Service = Service;
1235
+ exports.SshKeyFormat = SshKeyFormat;
1236
+ exports.StorageKeys = StorageKeys;
1237
+ exports.TokenAuth = TokenAuth;
1238
+ exports.TransportError = TransportError;
1239
+ exports.base64UrlEncode = base64UrlEncode;
1240
+ exports.buildRequestUrl = buildRequestUrl;
1241
+ exports.buildServiceUrl = buildServiceUrl;
1242
+ exports.generateCodeChallenge = generateCodeChallenge;
1243
+ exports.generateCodeVerifier = generateCodeVerifier;
1244
+ exports.generateState = generateState;
1245
+ exports.handlePkceCallback = handlePkceCallback;
1246
+ exports.validateHost = validateHost;
1247
+
1248
+ //# sourceMappingURL=index.cjs.map