@qnsp/tenant-sdk 0.3.0 → 0.3.2
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 +21 -7
- package/README.md +17 -51
- package/dist/index.d.ts +389 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +436 -11
- package/dist/index.js.map +1 -1
- package/dist/sdk-package-version.d.ts +2 -0
- package/dist/sdk-package-version.d.ts.map +1 -0
- package/dist/sdk-package-version.js +6 -0
- package/dist/sdk-package-version.js.map +1 -0
- package/package.json +72 -39
- package/src/index.test.ts +0 -170
- package/src/index.ts +0 -916
- package/src/observability.ts +0 -152
- package/src/validation.ts +0 -21
- package/tsconfig.build.json +0 -10
- package/tsconfig.json +0 -10
- package/tsconfig.tsbuildinfo +0 -1
- package/vitest.config.ts +0 -9
package/dist/index.js
CHANGED
|
@@ -1,31 +1,168 @@
|
|
|
1
1
|
import { performance } from "node:perf_hooks";
|
|
2
|
+
import { activateSdk } from "@qnsp/sdk-activation";
|
|
2
3
|
import { createTenantClientTelemetry, isTenantClientTelemetry } from "./observability.js";
|
|
4
|
+
import { SDK_PACKAGE_VERSION } from "./sdk-package-version.js";
|
|
3
5
|
import { validateUUID } from "./validation.js";
|
|
6
|
+
/**
|
|
7
|
+
* @qnsp/tenant-sdk
|
|
8
|
+
*
|
|
9
|
+
* TypeScript SDK client for the QNSP tenant-service API.
|
|
10
|
+
* Provides a high-level interface for tenant lifecycle and subscription management.
|
|
11
|
+
*/
|
|
12
|
+
/** Default QNSP cloud API base URL. Get a free API key at https://cloud.qnsp.cuilabs.io/signup */
|
|
13
|
+
export const DEFAULT_BASE_URL = "https://api.qnsp.cuilabs.io";
|
|
4
14
|
/**
|
|
5
15
|
* Default algorithms per crypto policy tier.
|
|
16
|
+
* These match the definitions in packages/security/src/crypto-policy.ts
|
|
17
|
+
* and determine which algorithms appear in the portal's Generate Key dropdown.
|
|
18
|
+
*
|
|
19
|
+
* default: All supported PQC algorithms (NIST-finalized + candidates via liboqs)
|
|
20
|
+
* strict: NIST-finalized/selected at higher security levels
|
|
21
|
+
* maximum: Highest-security NIST-finalized only
|
|
22
|
+
* government: FIPS-finalized only (no draft standards)
|
|
6
23
|
*/
|
|
7
24
|
export const CRYPTO_POLICY_ALGORITHMS = {
|
|
8
25
|
default: {
|
|
9
|
-
kemAlgorithms: [
|
|
10
|
-
|
|
26
|
+
kemAlgorithms: [
|
|
27
|
+
// FIPS 203 — ML-KEM (NIST finalized)
|
|
28
|
+
"kyber-512",
|
|
29
|
+
"kyber-768",
|
|
30
|
+
"kyber-1024",
|
|
31
|
+
// HQC (NIST selected March 2025)
|
|
32
|
+
"hqc-128",
|
|
33
|
+
"hqc-192",
|
|
34
|
+
"hqc-256",
|
|
35
|
+
// BIKE (NIST Round 4 candidate)
|
|
36
|
+
"bike-l1",
|
|
37
|
+
"bike-l3",
|
|
38
|
+
"bike-l5",
|
|
39
|
+
// Classic McEliece (ISO standard)
|
|
40
|
+
"mceliece-348864",
|
|
41
|
+
"mceliece-460896",
|
|
42
|
+
"mceliece-6688128",
|
|
43
|
+
"mceliece-6960119",
|
|
44
|
+
"mceliece-8192128",
|
|
45
|
+
// FrodoKEM (ISO standard)
|
|
46
|
+
"frodokem-640-aes",
|
|
47
|
+
"frodokem-640-shake",
|
|
48
|
+
"frodokem-976-aes",
|
|
49
|
+
"frodokem-976-shake",
|
|
50
|
+
"frodokem-1344-aes",
|
|
51
|
+
"frodokem-1344-shake",
|
|
52
|
+
// NTRU (lattice-based)
|
|
53
|
+
"ntru-hps-2048-509",
|
|
54
|
+
"ntru-hps-2048-677",
|
|
55
|
+
"ntru-hps-4096-821",
|
|
56
|
+
"ntru-hps-4096-1229",
|
|
57
|
+
"ntru-hrss-701",
|
|
58
|
+
"ntru-hrss-1373",
|
|
59
|
+
// NTRU-Prime
|
|
60
|
+
"sntrup761",
|
|
61
|
+
],
|
|
62
|
+
signatureAlgorithms: [
|
|
63
|
+
// FIPS 204 — ML-DSA (NIST finalized)
|
|
64
|
+
"dilithium-2",
|
|
65
|
+
"dilithium-3",
|
|
66
|
+
"dilithium-5",
|
|
67
|
+
// FIPS 205 — SLH-DSA (NIST finalized, SHA-2 variants)
|
|
68
|
+
"sphincs-sha2-128f-simple",
|
|
69
|
+
"sphincs-sha2-128s-simple",
|
|
70
|
+
"sphincs-sha2-192f-simple",
|
|
71
|
+
"sphincs-sha2-192s-simple",
|
|
72
|
+
"sphincs-sha2-256f-simple",
|
|
73
|
+
"sphincs-sha2-256s-simple",
|
|
74
|
+
// FIPS 205 — SLH-DSA (NIST finalized, SHAKE variants)
|
|
75
|
+
"sphincs-shake-128f-simple",
|
|
76
|
+
"sphincs-shake-128s-simple",
|
|
77
|
+
"sphincs-shake-192f-simple",
|
|
78
|
+
"sphincs-shake-192s-simple",
|
|
79
|
+
"sphincs-shake-256f-simple",
|
|
80
|
+
"sphincs-shake-256s-simple",
|
|
81
|
+
// FN-DSA / Falcon (FIPS 206 draft)
|
|
82
|
+
"falcon-512",
|
|
83
|
+
"falcon-1024",
|
|
84
|
+
// MAYO (NIST Additional Signatures Round 2)
|
|
85
|
+
"mayo-1",
|
|
86
|
+
"mayo-2",
|
|
87
|
+
"mayo-3",
|
|
88
|
+
"mayo-5",
|
|
89
|
+
// CROSS (NIST Additional Signatures Round 2)
|
|
90
|
+
"cross-rsdp-128-balanced",
|
|
91
|
+
"cross-rsdp-128-fast",
|
|
92
|
+
"cross-rsdp-128-small",
|
|
93
|
+
"cross-rsdp-192-balanced",
|
|
94
|
+
"cross-rsdp-192-fast",
|
|
95
|
+
"cross-rsdp-192-small",
|
|
96
|
+
"cross-rsdp-256-balanced",
|
|
97
|
+
"cross-rsdp-256-fast",
|
|
98
|
+
"cross-rsdp-256-small",
|
|
99
|
+
"cross-rsdpg-128-balanced",
|
|
100
|
+
"cross-rsdpg-128-fast",
|
|
101
|
+
"cross-rsdpg-128-small",
|
|
102
|
+
"cross-rsdpg-192-balanced",
|
|
103
|
+
"cross-rsdpg-192-fast",
|
|
104
|
+
"cross-rsdpg-192-small",
|
|
105
|
+
"cross-rsdpg-256-balanced",
|
|
106
|
+
"cross-rsdpg-256-fast",
|
|
107
|
+
"cross-rsdpg-256-small",
|
|
108
|
+
// UOV (NIST Additional Signatures Round 2)
|
|
109
|
+
"ov-Is",
|
|
110
|
+
"ov-Ip",
|
|
111
|
+
"ov-III",
|
|
112
|
+
"ov-V",
|
|
113
|
+
"ov-Is-pkc",
|
|
114
|
+
"ov-Ip-pkc",
|
|
115
|
+
"ov-III-pkc",
|
|
116
|
+
"ov-V-pkc",
|
|
117
|
+
"ov-Is-pkc-skc",
|
|
118
|
+
"ov-Ip-pkc-skc",
|
|
119
|
+
"ov-III-pkc-skc",
|
|
120
|
+
"ov-V-pkc-skc",
|
|
121
|
+
// SNOVA (NIST Additional Signatures Round 2)
|
|
122
|
+
"snova-24-5-4",
|
|
123
|
+
"snova-24-5-4-shake",
|
|
124
|
+
"snova-24-5-4-esk",
|
|
125
|
+
"snova-24-5-4-shake-esk",
|
|
126
|
+
"snova-25-8-3",
|
|
127
|
+
"snova-37-17-2",
|
|
128
|
+
"snova-37-8-4",
|
|
129
|
+
"snova-24-5-5",
|
|
130
|
+
"snova-56-25-2",
|
|
131
|
+
"snova-49-11-3",
|
|
132
|
+
"snova-60-10-4",
|
|
133
|
+
"snova-29-6-5",
|
|
134
|
+
],
|
|
11
135
|
defaultKemAlgorithm: "kyber-768",
|
|
12
136
|
defaultSignatureAlgorithm: "dilithium-3",
|
|
13
137
|
},
|
|
14
138
|
strict: {
|
|
15
|
-
kemAlgorithms: ["kyber-768", "kyber-1024"],
|
|
16
|
-
signatureAlgorithms: [
|
|
139
|
+
kemAlgorithms: ["kyber-768", "kyber-1024", "hqc-192", "hqc-256"],
|
|
140
|
+
signatureAlgorithms: [
|
|
141
|
+
"dilithium-3",
|
|
142
|
+
"dilithium-5",
|
|
143
|
+
"falcon-1024",
|
|
144
|
+
"sphincs-sha2-256f-simple",
|
|
145
|
+
"sphincs-sha2-256s-simple",
|
|
146
|
+
"sphincs-shake-256f-simple",
|
|
147
|
+
"sphincs-shake-256s-simple",
|
|
148
|
+
],
|
|
17
149
|
defaultKemAlgorithm: "kyber-768",
|
|
18
150
|
defaultSignatureAlgorithm: "dilithium-3",
|
|
19
151
|
},
|
|
20
152
|
maximum: {
|
|
21
|
-
kemAlgorithms: ["kyber-1024"],
|
|
22
|
-
signatureAlgorithms: [
|
|
153
|
+
kemAlgorithms: ["kyber-1024", "hqc-256"],
|
|
154
|
+
signatureAlgorithms: [
|
|
155
|
+
"dilithium-5",
|
|
156
|
+
"falcon-1024",
|
|
157
|
+
"sphincs-sha2-256f-simple",
|
|
158
|
+
"sphincs-shake-256f-simple",
|
|
159
|
+
],
|
|
23
160
|
defaultKemAlgorithm: "kyber-1024",
|
|
24
161
|
defaultSignatureAlgorithm: "dilithium-5",
|
|
25
162
|
},
|
|
26
163
|
government: {
|
|
27
164
|
kemAlgorithms: ["kyber-1024"],
|
|
28
|
-
signatureAlgorithms: ["dilithium-5", "sphincs-shake-256f-simple"],
|
|
165
|
+
signatureAlgorithms: ["dilithium-5", "sphincs-sha2-256f-simple", "sphincs-shake-256f-simple"],
|
|
29
166
|
defaultKemAlgorithm: "kyber-1024",
|
|
30
167
|
defaultSignatureAlgorithm: "dilithium-5",
|
|
31
168
|
},
|
|
@@ -158,19 +295,41 @@ export class TenantClient {
|
|
|
158
295
|
config;
|
|
159
296
|
telemetry;
|
|
160
297
|
targetService;
|
|
298
|
+
activationPromise = null;
|
|
299
|
+
activationConfig;
|
|
300
|
+
async ensureActivated() {
|
|
301
|
+
if (!this.activationPromise) {
|
|
302
|
+
this.activationPromise = activateSdk(this.activationConfig).then(() => undefined);
|
|
303
|
+
}
|
|
304
|
+
await this.activationPromise;
|
|
305
|
+
}
|
|
161
306
|
constructor(config) {
|
|
162
307
|
if (!config.apiKey || config.apiKey.trim().length === 0) {
|
|
163
308
|
throw new Error("QNSP Tenant SDK: apiKey is required. " +
|
|
164
309
|
"Get your free API key at https://cloud.qnsp.cuilabs.io/signup — " +
|
|
165
|
-
"no credit card required (FREE tier:
|
|
310
|
+
"no credit card required (FREE tier: 10 GB storage, 50,000 API calls/month). " +
|
|
166
311
|
"Docs: https://docs.qnsp.cuilabs.io/sdk/tenant-sdk");
|
|
167
312
|
}
|
|
168
|
-
const baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
169
|
-
// Enforce HTTPS in production (allow HTTP
|
|
313
|
+
const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
314
|
+
// Enforce HTTPS in production (allow HTTP for localhost in development and
|
|
315
|
+
// for internal service-mesh hostnames — e.g. *.internal — which are on a
|
|
316
|
+
// private VPC network and do not require TLS termination at the transport layer).
|
|
170
317
|
if (!baseUrl.startsWith("https://")) {
|
|
171
318
|
const isLocalhost = baseUrl.startsWith("http://localhost") || baseUrl.startsWith("http://127.0.0.1");
|
|
319
|
+
let isInternalService = false;
|
|
320
|
+
try {
|
|
321
|
+
const parsed = new URL(baseUrl);
|
|
322
|
+
isInternalService =
|
|
323
|
+
parsed.protocol === "http:" &&
|
|
324
|
+
(parsed.hostname.endsWith(".internal") ||
|
|
325
|
+
parsed.hostname === "localhost" ||
|
|
326
|
+
parsed.hostname === "127.0.0.1");
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
// ignore; invalid URL will be caught later by fetch
|
|
330
|
+
}
|
|
172
331
|
const isDevelopment = process.env["NODE_ENV"] === "development" || process.env["NODE_ENV"] === "test";
|
|
173
|
-
if (!isLocalhost || !isDevelopment) {
|
|
332
|
+
if ((!isLocalhost || !isDevelopment) && !isInternalService) {
|
|
174
333
|
throw new Error("baseUrl must use HTTPS in production. HTTP is only allowed for localhost in development.");
|
|
175
334
|
}
|
|
176
335
|
}
|
|
@@ -192,6 +351,12 @@ export class TenantClient {
|
|
|
192
351
|
catch {
|
|
193
352
|
this.targetService = "tenant-service";
|
|
194
353
|
}
|
|
354
|
+
this.activationConfig = {
|
|
355
|
+
apiKey: config.apiKey,
|
|
356
|
+
sdkId: "tenant-sdk",
|
|
357
|
+
sdkVersion: SDK_PACKAGE_VERSION,
|
|
358
|
+
platformUrl: baseUrl,
|
|
359
|
+
};
|
|
195
360
|
}
|
|
196
361
|
async request(method, path, options) {
|
|
197
362
|
return this.requestWithRetry(method, path, options, 0);
|
|
@@ -296,6 +461,7 @@ export class TenantClient {
|
|
|
296
461
|
* Requires PQC-signed security envelope and optional signature.
|
|
297
462
|
*/
|
|
298
463
|
async createTenant(request) {
|
|
464
|
+
await this.ensureActivated();
|
|
299
465
|
// Validation is handled by the service, but we validate format here for early feedback
|
|
300
466
|
return this.request("POST", "/tenant/v1/tenants", {
|
|
301
467
|
body: {
|
|
@@ -319,6 +485,7 @@ export class TenantClient {
|
|
|
319
485
|
*/
|
|
320
486
|
async updateTenant(id, request) {
|
|
321
487
|
validateUUID(id, "id");
|
|
488
|
+
await this.ensureActivated();
|
|
322
489
|
return this.request("PATCH", `/tenant/v1/tenants/${id}`, {
|
|
323
490
|
body: {
|
|
324
491
|
...(request.plan !== undefined ? { plan: request.plan } : {}),
|
|
@@ -338,6 +505,7 @@ export class TenantClient {
|
|
|
338
505
|
*/
|
|
339
506
|
async getTenant(id) {
|
|
340
507
|
validateUUID(id, "id");
|
|
508
|
+
await this.ensureActivated();
|
|
341
509
|
return this.request("GET", `/tenant/v1/tenants/${id}`, {
|
|
342
510
|
operation: "getTenant",
|
|
343
511
|
});
|
|
@@ -347,6 +515,7 @@ export class TenantClient {
|
|
|
347
515
|
* Returns a list of tenants and an optional next cursor for pagination.
|
|
348
516
|
*/
|
|
349
517
|
async listTenants(options) {
|
|
518
|
+
await this.ensureActivated();
|
|
350
519
|
const params = new URLSearchParams();
|
|
351
520
|
if (options?.limit !== undefined) {
|
|
352
521
|
params.set("limit", String(options.limit));
|
|
@@ -367,6 +536,7 @@ export class TenantClient {
|
|
|
367
536
|
*/
|
|
368
537
|
async getTenantCryptoPolicy(tenantId) {
|
|
369
538
|
validateUUID(tenantId, "tenantId");
|
|
539
|
+
await this.ensureActivated();
|
|
370
540
|
return this.request("GET", `/tenant/v1/tenants/${tenantId}/crypto-policy`, {
|
|
371
541
|
operation: "getTenantCryptoPolicy",
|
|
372
542
|
});
|
|
@@ -377,6 +547,7 @@ export class TenantClient {
|
|
|
377
547
|
*/
|
|
378
548
|
async getTenantCryptoPolicyV1(tenantId) {
|
|
379
549
|
validateUUID(tenantId, "tenantId");
|
|
550
|
+
await this.ensureActivated();
|
|
380
551
|
return this.request("GET", `/tenant/v1/tenants/${tenantId}/crypto-policy-v1`, {
|
|
381
552
|
operation: "getTenantCryptoPolicyV1",
|
|
382
553
|
});
|
|
@@ -386,6 +557,7 @@ export class TenantClient {
|
|
|
386
557
|
*/
|
|
387
558
|
async listTenantCryptoPolicyV1History(tenantId, options) {
|
|
388
559
|
validateUUID(tenantId, "tenantId");
|
|
560
|
+
await this.ensureActivated();
|
|
389
561
|
const params = new URLSearchParams();
|
|
390
562
|
if (options?.limit !== undefined) {
|
|
391
563
|
params.set("limit", String(options.limit));
|
|
@@ -404,6 +576,7 @@ export class TenantClient {
|
|
|
404
576
|
*/
|
|
405
577
|
async upsertTenantCryptoPolicy(tenantId, policy) {
|
|
406
578
|
validateUUID(tenantId, "tenantId");
|
|
579
|
+
await this.ensureActivated();
|
|
407
580
|
return this.request("PUT", `/tenant/v1/tenants/${tenantId}/crypto-policy`, {
|
|
408
581
|
body: {
|
|
409
582
|
policyTier: policy.policyTier,
|
|
@@ -426,6 +599,7 @@ export class TenantClient {
|
|
|
426
599
|
*/
|
|
427
600
|
async updateTenantCryptoPolicyV1(tenantId, policy, etag) {
|
|
428
601
|
validateUUID(tenantId, "tenantId");
|
|
602
|
+
await this.ensureActivated();
|
|
429
603
|
if (!etag) {
|
|
430
604
|
throw new Error("etag is required for updateTenantCryptoPolicyV1");
|
|
431
605
|
}
|
|
@@ -442,6 +616,7 @@ export class TenantClient {
|
|
|
442
616
|
*/
|
|
443
617
|
async enableTier0Legacy(tenantId, input, etag) {
|
|
444
618
|
validateUUID(tenantId, "tenantId");
|
|
619
|
+
await this.ensureActivated();
|
|
445
620
|
if (!etag) {
|
|
446
621
|
throw new Error("etag is required for enableTier0Legacy");
|
|
447
622
|
}
|
|
@@ -458,6 +633,7 @@ export class TenantClient {
|
|
|
458
633
|
*/
|
|
459
634
|
async disableTier0Legacy(tenantId, etag) {
|
|
460
635
|
validateUUID(tenantId, "tenantId");
|
|
636
|
+
await this.ensureActivated();
|
|
461
637
|
if (!etag) {
|
|
462
638
|
throw new Error("etag is required for disableTier0Legacy");
|
|
463
639
|
}
|
|
@@ -474,6 +650,7 @@ export class TenantClient {
|
|
|
474
650
|
*/
|
|
475
651
|
async enableTier4Experimental(tenantId, input, etag) {
|
|
476
652
|
validateUUID(tenantId, "tenantId");
|
|
653
|
+
await this.ensureActivated();
|
|
477
654
|
if (!etag) {
|
|
478
655
|
throw new Error("etag is required for enableTier4Experimental");
|
|
479
656
|
}
|
|
@@ -490,6 +667,7 @@ export class TenantClient {
|
|
|
490
667
|
*/
|
|
491
668
|
async rollbackTenantCryptoPolicyV1(tenantId, input, etag) {
|
|
492
669
|
validateUUID(tenantId, "tenantId");
|
|
670
|
+
await this.ensureActivated();
|
|
493
671
|
if (!etag) {
|
|
494
672
|
throw new Error("etag is required for rollbackTenantCryptoPolicyV1");
|
|
495
673
|
}
|
|
@@ -512,6 +690,8 @@ export class TenantClient {
|
|
|
512
690
|
* Convenience method that fetches the policy and returns the allowed algorithms.
|
|
513
691
|
*/
|
|
514
692
|
async getAllowedKemAlgorithms(tenantId) {
|
|
693
|
+
validateUUID(tenantId, "tenantId");
|
|
694
|
+
await this.ensureActivated();
|
|
515
695
|
const policy = await this.getTenantCryptoPolicy(tenantId);
|
|
516
696
|
if (policy.customAllowedKemAlgorithms && policy.customAllowedKemAlgorithms.length > 0) {
|
|
517
697
|
return policy.customAllowedKemAlgorithms;
|
|
@@ -523,6 +703,8 @@ export class TenantClient {
|
|
|
523
703
|
* Convenience method that fetches the policy and returns the allowed algorithms.
|
|
524
704
|
*/
|
|
525
705
|
async getAllowedSignatureAlgorithms(tenantId) {
|
|
706
|
+
validateUUID(tenantId, "tenantId");
|
|
707
|
+
await this.ensureActivated();
|
|
526
708
|
const policy = await this.getTenantCryptoPolicy(tenantId);
|
|
527
709
|
if (policy.customAllowedSignatureAlgorithms &&
|
|
528
710
|
policy.customAllowedSignatureAlgorithms.length > 0) {
|
|
@@ -534,6 +716,8 @@ export class TenantClient {
|
|
|
534
716
|
* Get the default KEM algorithm for a tenant based on their crypto policy tier.
|
|
535
717
|
*/
|
|
536
718
|
async getDefaultKemAlgorithm(tenantId) {
|
|
719
|
+
validateUUID(tenantId, "tenantId");
|
|
720
|
+
await this.ensureActivated();
|
|
537
721
|
const policy = await this.getTenantCryptoPolicy(tenantId);
|
|
538
722
|
return CRYPTO_POLICY_ALGORITHMS[policy.policyTier].defaultKemAlgorithm;
|
|
539
723
|
}
|
|
@@ -541,9 +725,250 @@ export class TenantClient {
|
|
|
541
725
|
* Get the default signature algorithm for a tenant based on their crypto policy tier.
|
|
542
726
|
*/
|
|
543
727
|
async getDefaultSignatureAlgorithm(tenantId) {
|
|
728
|
+
validateUUID(tenantId, "tenantId");
|
|
729
|
+
await this.ensureActivated();
|
|
544
730
|
const policy = await this.getTenantCryptoPolicy(tenantId);
|
|
545
731
|
return CRYPTO_POLICY_ALGORITHMS[policy.policyTier].defaultSignatureAlgorithm;
|
|
546
732
|
}
|
|
733
|
+
/**
|
|
734
|
+
* Record a health snapshot for a tenant.
|
|
735
|
+
*/
|
|
736
|
+
async recordHealthSnapshot(request) {
|
|
737
|
+
validateUUID(request.tenantId, "tenantId");
|
|
738
|
+
await this.ensureActivated();
|
|
739
|
+
return this.request("POST", `/tenant/v1/tenants/${request.tenantId}/health/snapshots`, {
|
|
740
|
+
body: { metrics: request.metrics },
|
|
741
|
+
operation: "recordHealthSnapshot",
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Get the current health status for a tenant.
|
|
746
|
+
*/
|
|
747
|
+
async getCurrentHealth(tenantId) {
|
|
748
|
+
validateUUID(tenantId, "tenantId");
|
|
749
|
+
await this.ensureActivated();
|
|
750
|
+
return this.request("GET", `/tenant/v1/tenants/${tenantId}/health`, {
|
|
751
|
+
operation: "getCurrentHealth",
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Get health trends for a tenant over time.
|
|
756
|
+
*/
|
|
757
|
+
async getHealthTrends(request) {
|
|
758
|
+
validateUUID(request.tenantId, "tenantId");
|
|
759
|
+
await this.ensureActivated();
|
|
760
|
+
const params = new URLSearchParams();
|
|
761
|
+
if (request.since !== undefined)
|
|
762
|
+
params.set("since", request.since);
|
|
763
|
+
if (request.until !== undefined)
|
|
764
|
+
params.set("until", request.until);
|
|
765
|
+
if (request.granularity !== undefined)
|
|
766
|
+
params.set("granularity", request.granularity);
|
|
767
|
+
const queryString = params.toString();
|
|
768
|
+
const path = queryString
|
|
769
|
+
? `/tenant/v1/tenants/${request.tenantId}/health/trends?${queryString}`
|
|
770
|
+
: `/tenant/v1/tenants/${request.tenantId}/health/trends`;
|
|
771
|
+
return this.request("GET", path, {
|
|
772
|
+
operation: "getHealthTrends",
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Create a health alert for a tenant.
|
|
777
|
+
*/
|
|
778
|
+
async createHealthAlert(request) {
|
|
779
|
+
validateUUID(request.tenantId, "tenantId");
|
|
780
|
+
await this.ensureActivated();
|
|
781
|
+
return this.request("POST", `/tenant/v1/tenants/${request.tenantId}/health/alerts`, {
|
|
782
|
+
body: {
|
|
783
|
+
severity: request.severity,
|
|
784
|
+
title: request.title,
|
|
785
|
+
description: request.description,
|
|
786
|
+
...(request.metric !== undefined ? { metric: request.metric } : {}),
|
|
787
|
+
...(request.threshold !== undefined ? { threshold: request.threshold } : {}),
|
|
788
|
+
...(request.currentValue !== undefined ? { currentValue: request.currentValue } : {}),
|
|
789
|
+
},
|
|
790
|
+
operation: "createHealthAlert",
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Acknowledge a health alert.
|
|
795
|
+
*/
|
|
796
|
+
async acknowledgeAlert(request) {
|
|
797
|
+
validateUUID(request.alertId, "alertId");
|
|
798
|
+
await this.ensureActivated();
|
|
799
|
+
return this.request("POST", `/tenant/v1/health/alerts/${request.alertId}/acknowledge`, {
|
|
800
|
+
body: {
|
|
801
|
+
acknowledgedBy: request.acknowledgedBy,
|
|
802
|
+
...(request.note !== undefined ? { note: request.note } : {}),
|
|
803
|
+
},
|
|
804
|
+
operation: "acknowledgeAlert",
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Record quota usage for a tenant.
|
|
809
|
+
*/
|
|
810
|
+
async recordQuotaUsage(request) {
|
|
811
|
+
validateUUID(request.tenantId, "tenantId");
|
|
812
|
+
await this.ensureActivated();
|
|
813
|
+
return this.request("POST", `/tenant/v1/tenants/${request.tenantId}/quotas/usage`, {
|
|
814
|
+
body: {
|
|
815
|
+
quotaName: request.quotaName,
|
|
816
|
+
usage: request.usage,
|
|
817
|
+
...(request.timestamp !== undefined ? { timestamp: request.timestamp } : {}),
|
|
818
|
+
},
|
|
819
|
+
operation: "recordQuotaUsage",
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Get current quota usage for a tenant.
|
|
824
|
+
*/
|
|
825
|
+
async getCurrentQuotas(tenantId) {
|
|
826
|
+
validateUUID(tenantId, "tenantId");
|
|
827
|
+
await this.ensureActivated();
|
|
828
|
+
return this.request("GET", `/tenant/v1/tenants/${tenantId}/quotas`, {
|
|
829
|
+
operation: "getCurrentQuotas",
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Get quota forecast for a tenant.
|
|
834
|
+
*/
|
|
835
|
+
async getForecast(request) {
|
|
836
|
+
validateUUID(request.tenantId, "tenantId");
|
|
837
|
+
await this.ensureActivated();
|
|
838
|
+
const params = new URLSearchParams();
|
|
839
|
+
if (request.quotaName !== undefined)
|
|
840
|
+
params.set("quotaName", request.quotaName);
|
|
841
|
+
if (request.horizonDays !== undefined)
|
|
842
|
+
params.set("horizonDays", String(request.horizonDays));
|
|
843
|
+
const queryString = params.toString();
|
|
844
|
+
const path = queryString
|
|
845
|
+
? `/tenant/v1/tenants/${request.tenantId}/quotas/forecast?${queryString}`
|
|
846
|
+
: `/tenant/v1/tenants/${request.tenantId}/quotas/forecast`;
|
|
847
|
+
return this.request("GET", path, {
|
|
848
|
+
operation: "getForecast",
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Get quota suggestions based on usage patterns.
|
|
853
|
+
*/
|
|
854
|
+
async getQuotaSuggestions(tenantId) {
|
|
855
|
+
validateUUID(tenantId, "tenantId");
|
|
856
|
+
await this.ensureActivated();
|
|
857
|
+
return this.request("GET", `/tenant/v1/tenants/${tenantId}/quotas/suggestions`, {
|
|
858
|
+
operation: "getQuotaSuggestions",
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Create an onboarding workflow template.
|
|
863
|
+
*/
|
|
864
|
+
async createWorkflowTemplate(request) {
|
|
865
|
+
await this.ensureActivated();
|
|
866
|
+
return this.request("POST", "/tenant/v1/onboarding/templates", {
|
|
867
|
+
body: request,
|
|
868
|
+
operation: "createWorkflowTemplate",
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Start onboarding for a tenant.
|
|
873
|
+
*/
|
|
874
|
+
async startOnboarding(request) {
|
|
875
|
+
validateUUID(request.tenantId, "tenantId");
|
|
876
|
+
await this.ensureActivated();
|
|
877
|
+
return this.request("POST", `/tenant/v1/tenants/${request.tenantId}/onboarding/start`, {
|
|
878
|
+
body: {
|
|
879
|
+
...(request.templateId !== undefined ? { templateId: request.templateId } : {}),
|
|
880
|
+
...(request.metadata !== undefined ? { metadata: request.metadata } : {}),
|
|
881
|
+
},
|
|
882
|
+
operation: "startOnboarding",
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Get onboarding status for a tenant.
|
|
887
|
+
*/
|
|
888
|
+
async getOnboardingStatus(tenantId) {
|
|
889
|
+
validateUUID(tenantId, "tenantId");
|
|
890
|
+
await this.ensureActivated();
|
|
891
|
+
return this.request("GET", `/tenant/v1/tenants/${tenantId}/onboarding/status`, {
|
|
892
|
+
operation: "getOnboardingStatus",
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Get onboarding statistics.
|
|
897
|
+
*/
|
|
898
|
+
async getOnboardingStats(options) {
|
|
899
|
+
await this.ensureActivated();
|
|
900
|
+
const params = new URLSearchParams();
|
|
901
|
+
if (options?.since !== undefined)
|
|
902
|
+
params.set("since", options.since);
|
|
903
|
+
if (options?.until !== undefined)
|
|
904
|
+
params.set("until", options.until);
|
|
905
|
+
const queryString = params.toString();
|
|
906
|
+
const path = queryString
|
|
907
|
+
? `/tenant/v1/onboarding/stats?${queryString}`
|
|
908
|
+
: "/tenant/v1/onboarding/stats";
|
|
909
|
+
return this.request("GET", path, {
|
|
910
|
+
operation: "getOnboardingStats",
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Create an isolation policy for a tenant.
|
|
915
|
+
*/
|
|
916
|
+
async createIsolationPolicy(request) {
|
|
917
|
+
validateUUID(request.tenantId, "tenantId");
|
|
918
|
+
await this.ensureActivated();
|
|
919
|
+
return this.request("POST", `/tenant/v1/tenants/${request.tenantId}/isolation/policies`, {
|
|
920
|
+
body: {
|
|
921
|
+
name: request.name,
|
|
922
|
+
...(request.description !== undefined ? { description: request.description } : {}),
|
|
923
|
+
level: request.level,
|
|
924
|
+
...(request.rules !== undefined ? { rules: request.rules } : {}),
|
|
925
|
+
...(request.enforcementMode !== undefined
|
|
926
|
+
? { enforcementMode: request.enforcementMode }
|
|
927
|
+
: {}),
|
|
928
|
+
},
|
|
929
|
+
operation: "createIsolationPolicy",
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Run an isolation audit for a tenant.
|
|
934
|
+
*/
|
|
935
|
+
async runIsolationAudit(request) {
|
|
936
|
+
validateUUID(request.tenantId, "tenantId");
|
|
937
|
+
await this.ensureActivated();
|
|
938
|
+
return this.request("POST", `/tenant/v1/tenants/${request.tenantId}/isolation/audit`, {
|
|
939
|
+
body: {
|
|
940
|
+
...(request.policyIds !== undefined ? { policyIds: request.policyIds } : {}),
|
|
941
|
+
...(request.categories !== undefined ? { categories: request.categories } : {}),
|
|
942
|
+
...(request.depth !== undefined ? { depth: request.depth } : {}),
|
|
943
|
+
},
|
|
944
|
+
operation: "runIsolationAudit",
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Get isolation findings for a tenant.
|
|
949
|
+
*/
|
|
950
|
+
async getIsolationFindings(request) {
|
|
951
|
+
validateUUID(request.tenantId, "tenantId");
|
|
952
|
+
await this.ensureActivated();
|
|
953
|
+
const params = new URLSearchParams();
|
|
954
|
+
if (request.runId !== undefined)
|
|
955
|
+
params.set("runId", request.runId);
|
|
956
|
+
if (request.severity !== undefined)
|
|
957
|
+
params.set("severity", request.severity);
|
|
958
|
+
if (request.category !== undefined)
|
|
959
|
+
params.set("category", request.category);
|
|
960
|
+
if (request.limit !== undefined)
|
|
961
|
+
params.set("limit", String(request.limit));
|
|
962
|
+
if (request.cursor !== undefined)
|
|
963
|
+
params.set("cursor", request.cursor);
|
|
964
|
+
const queryString = params.toString();
|
|
965
|
+
const path = queryString
|
|
966
|
+
? `/tenant/v1/tenants/${request.tenantId}/isolation/findings?${queryString}`
|
|
967
|
+
: `/tenant/v1/tenants/${request.tenantId}/isolation/findings`;
|
|
968
|
+
return this.request("GET", path, {
|
|
969
|
+
operation: "getIsolationFindings",
|
|
970
|
+
});
|
|
971
|
+
}
|
|
547
972
|
}
|
|
548
973
|
export * from "./observability.js";
|
|
549
974
|
export * from "./validation.js";
|