@epochcore/qaas-sdk 1.0.0 → 1.2.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/index.js CHANGED
@@ -23,91 +23,232 @@ __export(index_exports, {
23
23
  ALGORITHMS: () => ALGORITHMS,
24
24
  QaaSClient: () => QaaSClient,
25
25
  QaaSError: () => QaaSError,
26
- qaas: () => qaas
26
+ QaaSRateLimitError: () => QaaSRateLimitError,
27
+ QaaSValidationError: () => QaaSValidationError,
28
+ SIGNATURE_MAX_AGE_MS: () => SIGNATURE_MAX_AGE_MS
27
29
  });
28
30
  module.exports = __toCommonJS(index_exports);
31
+ var SDK_VERSION = "1.2.0";
32
+ var SDK_USER_AGENT = `EpochCore-QaaS-SDK/${SDK_VERSION}`;
33
+ var _crypto = typeof globalThis.crypto !== "undefined" ? globalThis.crypto : require("crypto").webcrypto;
34
+ var MIN_TIMEOUT_MS = 5e3;
35
+ var MAX_TIMEOUT_MS = 12e4;
36
+ var DEFAULT_TIMEOUT_MS = 3e4;
37
+ var SIGNATURE_MAX_AGE_MS = 3e5;
38
+ var DEFAULT_RATE_LIMIT = 10;
39
+ var DEFAULT_RATE_BURST = 20;
40
+ var MAX_BODY_SIZE = 1048576;
41
+ var MAX_SHOTS = 1e5;
42
+ var MIN_SHOTS = 1;
43
+ var MAX_ALGORITHM_ID_LENGTH = 64;
44
+ var MAX_RETRY_ATTEMPTS = 3;
45
+ var RETRY_BASE_MS = 500;
46
+ var VALID_BACKENDS = /* @__PURE__ */ new Set([
47
+ "simulator",
48
+ "qiskit_aer",
49
+ "cirq",
50
+ "pennylane",
51
+ "ibm_quantum"
52
+ ]);
53
+ var TokenBucket = class {
54
+ constructor(capacity, refillRate) {
55
+ this.capacity = capacity;
56
+ this.refillRate = refillRate;
57
+ this.tokens = capacity;
58
+ this.lastRefill = Date.now();
59
+ }
60
+ tryConsume() {
61
+ this.refill();
62
+ if (this.tokens >= 1) {
63
+ this.tokens -= 1;
64
+ return true;
65
+ }
66
+ return false;
67
+ }
68
+ refill() {
69
+ const now = Date.now();
70
+ const elapsed = (now - this.lastRefill) / 1e3;
71
+ this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRate);
72
+ this.lastRefill = now;
73
+ }
74
+ };
75
+ async function hmacSign(secret, message) {
76
+ const enc = new TextEncoder();
77
+ const key = await _crypto.subtle.importKey(
78
+ "raw",
79
+ enc.encode(secret),
80
+ { name: "HMAC", hash: "SHA-256" },
81
+ false,
82
+ ["sign"]
83
+ );
84
+ const sig = await _crypto.subtle.sign("HMAC", key, enc.encode(message));
85
+ return Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
86
+ }
87
+ function generateNonce() {
88
+ const bytes = new Uint8Array(16);
89
+ _crypto.getRandomValues(bytes);
90
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
91
+ }
92
+ function validateRunRequest(req) {
93
+ if (!req.algorithm_id || typeof req.algorithm_id !== "string") {
94
+ throw new QaaSValidationError("algorithm_id is required and must be a string");
95
+ }
96
+ if (req.algorithm_id.length > MAX_ALGORITHM_ID_LENGTH) {
97
+ throw new QaaSValidationError(`algorithm_id exceeds max length of ${MAX_ALGORITHM_ID_LENGTH}`);
98
+ }
99
+ if (!/^[A-Z0-9_]+$/.test(req.algorithm_id)) {
100
+ throw new QaaSValidationError("algorithm_id must contain only uppercase letters, digits, and underscores");
101
+ }
102
+ if (req.backend !== void 0) {
103
+ if (!VALID_BACKENDS.has(req.backend)) {
104
+ throw new QaaSValidationError(
105
+ `Invalid backend "${req.backend}". Must be one of: ${[...VALID_BACKENDS].join(", ")}`
106
+ );
107
+ }
108
+ }
109
+ if (req.shots !== void 0) {
110
+ if (typeof req.shots !== "number" || !Number.isInteger(req.shots)) {
111
+ throw new QaaSValidationError("shots must be an integer");
112
+ }
113
+ if (req.shots < MIN_SHOTS || req.shots > MAX_SHOTS) {
114
+ throw new QaaSValidationError(`shots must be between ${MIN_SHOTS} and ${MAX_SHOTS}`);
115
+ }
116
+ }
117
+ if (!req.parameters || typeof req.parameters !== "object" || Array.isArray(req.parameters)) {
118
+ throw new QaaSValidationError("parameters must be a non-null object");
119
+ }
120
+ const serialized = JSON.stringify(req.parameters);
121
+ if (serialized.length > MAX_BODY_SIZE) {
122
+ throw new QaaSValidationError(`parameters exceed max size of ${MAX_BODY_SIZE} bytes`);
123
+ }
124
+ }
29
125
  var QaaSClient = class {
30
126
  constructor(config = {}) {
31
- this.baseUrl = config.baseUrl || "https://api.qaas.epochcoreqcs.com";
127
+ this.baseUrl = (config.baseUrl || "https://api.qaas.epochcoreqcs.com").replace(/\/+$/, "");
32
128
  this.apiKey = config.apiKey;
33
- this.timeout = config.timeout || 3e4;
129
+ this.apiSecret = config.apiSecret;
130
+ this.enableSigning = config.enableSigning ?? !!config.apiSecret;
131
+ this.maxRetries = Math.min(Math.max(config.maxRetries ?? MAX_RETRY_ATTEMPTS, 0), 5);
132
+ const raw = config.timeout ?? DEFAULT_TIMEOUT_MS;
133
+ this.timeout = Math.min(Math.max(raw, MIN_TIMEOUT_MS), MAX_TIMEOUT_MS);
134
+ this.rateLimiter = new TokenBucket(
135
+ config.rateBurst ?? DEFAULT_RATE_BURST,
136
+ config.rateLimit ?? DEFAULT_RATE_LIMIT
137
+ );
34
138
  }
35
- async request(path, options = {}) {
139
+ async request(method, path, body) {
140
+ if (!this.rateLimiter.tryConsume()) {
141
+ throw new QaaSRateLimitError("Client-side rate limit exceeded. Wait and retry.");
142
+ }
143
+ if (body && body.length > MAX_BODY_SIZE) {
144
+ throw new QaaSValidationError(`Request body exceeds max size of ${MAX_BODY_SIZE} bytes`);
145
+ }
146
+ const requestId = generateNonce();
147
+ const timestamp = Date.now().toString();
36
148
  const headers = {
37
149
  "Content-Type": "application/json",
38
- ...options.headers || {}
150
+ "User-Agent": SDK_USER_AGENT,
151
+ "X-Request-Id": requestId,
152
+ "X-Timestamp": timestamp
39
153
  };
40
154
  if (this.apiKey) {
41
155
  headers["X-API-Key"] = this.apiKey;
42
156
  }
43
- const controller = new AbortController();
44
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
45
- try {
46
- const response = await fetch(`${this.baseUrl}${path}`, {
47
- ...options,
48
- headers,
49
- signal: controller.signal
50
- });
51
- if (!response.ok) {
52
- const error = await response.json().catch(() => ({ error: "Unknown error" }));
53
- throw new QaaSError(
54
- error.error || `Request failed with status ${response.status}`,
55
- response.status,
56
- error
157
+ if (this.enableSigning && this.apiSecret) {
158
+ const nonce = generateNonce();
159
+ const sigPayload = [method, path, timestamp, nonce, body || ""].join("\n");
160
+ const signature = await hmacSign(this.apiSecret, sigPayload);
161
+ headers["X-Signature"] = signature;
162
+ headers["X-Nonce"] = nonce;
163
+ }
164
+ let lastError;
165
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
166
+ if (attempt > 0) {
167
+ const delay = RETRY_BASE_MS * Math.pow(2, attempt - 1);
168
+ const jitter = Math.random() * delay * 0.5;
169
+ await new Promise((r) => setTimeout(r, delay + jitter));
170
+ }
171
+ const controller = new AbortController();
172
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
173
+ try {
174
+ const response = await fetch(`${this.baseUrl}${path}`, {
175
+ method,
176
+ headers,
177
+ body: body || void 0,
178
+ signal: controller.signal
179
+ });
180
+ if (response.status === 429 && attempt < this.maxRetries) {
181
+ const retryAfter = response.headers.get("Retry-After");
182
+ if (retryAfter) {
183
+ await new Promise((r) => setTimeout(r, parseInt(retryAfter, 10) * 1e3));
184
+ }
185
+ lastError = new QaaSError("Rate limited by server", 429);
186
+ continue;
187
+ }
188
+ if ([502, 503, 504].includes(response.status) && attempt < this.maxRetries) {
189
+ lastError = new QaaSError(`Server error ${response.status}`, response.status);
190
+ continue;
191
+ }
192
+ if (!response.ok) {
193
+ const error = await response.json().catch(() => ({}));
194
+ throw new QaaSError(
195
+ typeof error.error === "string" ? error.error : `Request failed (${response.status})`,
196
+ response.status
197
+ );
198
+ }
199
+ return await response.json();
200
+ } catch (err) {
201
+ if (err instanceof QaaSError) throw err;
202
+ if (err instanceof QaaSValidationError) throw err;
203
+ if (err instanceof DOMException && err.name === "AbortError") {
204
+ lastError = new QaaSError(`Request timed out after ${this.timeout}ms`, 408);
205
+ if (attempt < this.maxRetries) continue;
206
+ throw lastError;
207
+ }
208
+ lastError = new QaaSError(
209
+ err instanceof Error ? err.message : "Network error",
210
+ 0
57
211
  );
212
+ if (attempt < this.maxRetries) continue;
213
+ } finally {
214
+ clearTimeout(timeoutId);
58
215
  }
59
- return await response.json();
60
- } finally {
61
- clearTimeout(timeoutId);
62
216
  }
217
+ throw lastError ?? new QaaSError("Request failed after retries", 0);
63
218
  }
64
- /**
65
- * Check API health and status
66
- */
219
+ // ═══════════════════════════════════════════════════════════════════
220
+ // API Methods
221
+ // ═══════════════════════════════════════════════════════════════════
67
222
  async health() {
68
- return this.request("/health");
223
+ return this.request("GET", "/health");
69
224
  }
70
- /**
71
- * Get pricing information for all tiers
72
- */
73
225
  async getPricing() {
74
- return this.request("/api/pricing");
226
+ return this.request("GET", "/api/pricing");
75
227
  }
76
- /**
77
- * Get available algorithms for your tier
78
- */
79
228
  async getAlgorithms() {
80
- return this.request("/api/quantum/algorithms");
229
+ return this.request("GET", "/api/quantum/algorithms");
81
230
  }
82
- /**
83
- * Get algorithms filtered by category
84
- */
85
231
  async getAlgorithmsByCategory(category) {
86
232
  const response = await this.getAlgorithms();
87
233
  return response.algorithms.filter((algo) => algo.category === category);
88
234
  }
89
- /**
90
- * Run a quantum algorithm
91
- */
92
235
  async runAlgorithm(request) {
93
- return this.request("/api/quantum/run", {
94
- method: "POST",
95
- body: JSON.stringify(request)
96
- });
236
+ validateRunRequest(request);
237
+ return this.request("POST", "/api/quantum/run", JSON.stringify(request));
97
238
  }
98
- /**
99
- * Get current usage statistics
100
- */
101
239
  async getUsage() {
102
- return this.request("/api/quantum/usage");
240
+ return this.request("GET", "/api/quantum/usage");
103
241
  }
104
- // ==========================================
105
- // Convenience methods for common algorithms
106
- // ==========================================
107
- /**
108
- * Run Markowitz portfolio optimization (QAOA)
109
- */
242
+ // ═══════════════════════════════════════════════════════════════════
243
+ // Convenience methods
244
+ // ═══════════════════════════════════════════════════════════════════
110
245
  async optimizePortfolio(params) {
246
+ if (!Array.isArray(params.assets) || params.assets.length === 0) {
247
+ throw new QaaSValidationError("assets must be a non-empty array of strings");
248
+ }
249
+ if (params.assets.length !== params.returns.length) {
250
+ throw new QaaSValidationError("assets and returns arrays must have equal length");
251
+ }
111
252
  return this.runAlgorithm({
112
253
  algorithm_id: "QAOA_PORT_001",
113
254
  parameters: {
@@ -119,24 +260,31 @@ var QaaSClient = class {
119
260
  backend: params.backend || "simulator"
120
261
  });
121
262
  }
122
- /**
123
- * Run Value at Risk (VaR) estimation
124
- */
125
263
  async estimateVaR(params) {
264
+ if (typeof params.portfolio_value !== "number" || params.portfolio_value <= 0) {
265
+ throw new QaaSValidationError("portfolio_value must be a positive number");
266
+ }
267
+ if (!Array.isArray(params.returns_history) || params.returns_history.length === 0) {
268
+ throw new QaaSValidationError("returns_history must be a non-empty array");
269
+ }
270
+ const cl = params.confidence_level ?? 0.95;
271
+ if (cl <= 0 || cl >= 1) {
272
+ throw new QaaSValidationError("confidence_level must be between 0 and 1 (exclusive)");
273
+ }
126
274
  return this.runAlgorithm({
127
275
  algorithm_id: "AE_PROB_004",
128
276
  parameters: {
129
277
  portfolio_value: params.portfolio_value,
130
278
  returns_history: params.returns_history,
131
- confidence_level: params.confidence_level ?? 0.95
279
+ confidence_level: cl
132
280
  },
133
281
  backend: params.backend || "simulator"
134
282
  });
135
283
  }
136
- /**
137
- * Run Grover search for arbitrage opportunities
138
- */
139
284
  async findArbitrage(params) {
285
+ if (!Array.isArray(params.price_matrix) || params.price_matrix.length === 0) {
286
+ throw new QaaSValidationError("price_matrix must be a non-empty 2D array");
287
+ }
140
288
  return this.runAlgorithm({
141
289
  algorithm_id: "GROVER_BOOL_003",
142
290
  parameters: {
@@ -146,23 +294,30 @@ var QaaSClient = class {
146
294
  backend: params.backend || "simulator"
147
295
  });
148
296
  }
149
- /**
150
- * Solve a QUBO problem
151
- */
152
297
  async solveQUBO(params) {
298
+ if (!Array.isArray(params.Q_matrix) || params.Q_matrix.length === 0) {
299
+ throw new QaaSValidationError("Q_matrix must be a non-empty 2D array");
300
+ }
301
+ const reads = params.num_reads ?? 1e3;
302
+ if (reads < 1 || reads > 1e5) {
303
+ throw new QaaSValidationError("num_reads must be between 1 and 100,000");
304
+ }
153
305
  return this.runAlgorithm({
154
306
  algorithm_id: "QAOA_QUBO_001",
155
307
  parameters: {
156
308
  Q: params.Q_matrix,
157
- num_reads: params.num_reads ?? 1e3
309
+ num_reads: reads
158
310
  },
159
311
  backend: params.backend || "simulator"
160
312
  });
161
313
  }
162
- /**
163
- * Run Monte Carlo amplitude estimation
164
- */
165
314
  async monteCarloEstimate(params) {
315
+ if (!params.target_function || typeof params.target_function !== "string") {
316
+ throw new QaaSValidationError("target_function is required");
317
+ }
318
+ if (params.target_function.length > 1e4) {
319
+ throw new QaaSValidationError("target_function exceeds max length of 10,000 characters");
320
+ }
166
321
  return this.runAlgorithm({
167
322
  algorithm_id: "AE_PROB_002",
168
323
  parameters: {
@@ -173,18 +328,132 @@ var QaaSClient = class {
173
328
  backend: params.backend || "simulator"
174
329
  });
175
330
  }
331
+ // ═══════════════════════════════════════════════════════════════════
332
+ // Quantum Teleportation (GROT) — via godel-task-router
333
+ // ═══════════════════════════════════════════════════════════════════
334
+ async quantumTeleport(params = {}) {
335
+ return this.request(
336
+ "POST",
337
+ "/v1/quantum/multi-hop/teleport",
338
+ JSON.stringify({
339
+ qubits: params.qubits ?? 8,
340
+ state: params.state ?? "ghz",
341
+ oscillation_cycles: params.oscillation_cycles ?? 3,
342
+ source_backend: params.source_backend,
343
+ destination_backend: params.destination_backend
344
+ })
345
+ );
346
+ }
347
+ async quantumTeleportStatus() {
348
+ return this.request("GET", "/v1/quantum/multi-hop/status");
349
+ }
350
+ async quantumTeleportPlan(params = {}) {
351
+ return this.request(
352
+ "POST",
353
+ "/v1/quantum/multi-hop/plan",
354
+ JSON.stringify({
355
+ qubits: params.qubits ?? 8,
356
+ state: params.state ?? "ghz",
357
+ strategy: params.strategy ?? "combined"
358
+ })
359
+ );
360
+ }
361
+ // ═══════════════════════════════════════════════════════════════════
362
+ // Quantum Random Number Generation (QRNG)
363
+ // ═══════════════════════════════════════════════════════════════════
364
+ /**
365
+ * Generate quantum random bits.
366
+ *
367
+ * @param params.bits - Number of random bits (1–4096, default 256)
368
+ * @param params.format - Output format: 'hex' | 'int' | 'bytes' (default 'hex')
369
+ */
370
+ async generateQRNG(params = {}) {
371
+ const bits = params.bits ?? 256;
372
+ if (typeof bits !== "number" || !Number.isInteger(bits)) {
373
+ throw new QaaSValidationError("bits must be an integer");
374
+ }
375
+ if (bits < 1 || bits > 4096) {
376
+ throw new QaaSValidationError("bits must be between 1 and 4096");
377
+ }
378
+ const format = params.format ?? "hex";
379
+ if (!["hex", "int", "bytes"].includes(format)) {
380
+ throw new QaaSValidationError("format must be hex, int, or bytes");
381
+ }
382
+ return this.request(
383
+ "POST",
384
+ "/v1/random/generate",
385
+ JSON.stringify({ bits, format })
386
+ );
387
+ }
388
+ /**
389
+ * Generate a quantum random number in the range [0, 1).
390
+ * Convenience wrapper over generateQRNG for normalized floats.
391
+ *
392
+ * @param count - How many floats to generate (1–1024, default 1)
393
+ * @returns Array of floats in [0, 1), each derived from 32 quantum bits
394
+ */
395
+ async generateQuantumFloats(count = 1) {
396
+ if (typeof count !== "number" || !Number.isInteger(count)) {
397
+ throw new QaaSValidationError("count must be an integer");
398
+ }
399
+ if (count < 1 || count > 1024) {
400
+ throw new QaaSValidationError("count must be between 1 and 1024");
401
+ }
402
+ const totalBits = count * 32;
403
+ const response = await this.generateQRNG({ bits: Math.min(totalBits, 4096), format: "bytes" });
404
+ const bytes = response.value;
405
+ const floats = [];
406
+ for (let i = 0; i < count && i * 4 + 3 < bytes.length; i++) {
407
+ const u32 = (bytes[i * 4] << 24 | bytes[i * 4 + 1] << 16 | bytes[i * 4 + 2] << 8 | bytes[i * 4 + 3]) >>> 0;
408
+ floats.push(u32 / 4294967295);
409
+ }
410
+ return floats;
411
+ }
412
+ /**
413
+ * Generate a quantum-random cryptographic key.
414
+ *
415
+ * @param length - Key length in bytes (1–64, default 32)
416
+ */
417
+ async generateQuantumKey(length = 32) {
418
+ if (typeof length !== "number" || !Number.isInteger(length)) {
419
+ throw new QaaSValidationError("length must be an integer");
420
+ }
421
+ if (length < 1 || length > 64) {
422
+ throw new QaaSValidationError("length must be between 1 and 64");
423
+ }
424
+ return this.request(
425
+ "POST",
426
+ "/v1/random/key",
427
+ JSON.stringify({ length })
428
+ );
429
+ }
430
+ /**
431
+ * Generate a quantum-random UUID v4.
432
+ */
433
+ async generateQuantumUUID() {
434
+ return this.request("POST", "/v1/random/uuid", "{}");
435
+ }
176
436
  };
177
437
  var QaaSError = class extends Error {
178
- constructor(message, statusCode, response) {
438
+ constructor(message, statusCode) {
179
439
  super(message);
180
440
  this.statusCode = statusCode;
181
- this.response = response;
182
441
  this.name = "QaaSError";
183
442
  }
184
443
  };
185
- var qaas = new QaaSClient();
444
+ var QaaSValidationError = class extends Error {
445
+ constructor(message) {
446
+ super(message);
447
+ this.name = "QaaSValidationError";
448
+ }
449
+ };
450
+ var QaaSRateLimitError = class extends Error {
451
+ constructor(message) {
452
+ super(message);
453
+ this.name = "QaaSRateLimitError";
454
+ }
455
+ };
186
456
  var ALGORITHMS = {
187
- // Amplitude Estimation
188
457
  AMPLITUDE_ESTIMATION: {
189
458
  PROB_001: "AE_PROB_001",
190
459
  PROB_002: "AE_PROB_002",
@@ -192,7 +461,6 @@ var ALGORITHMS = {
192
461
  VAR: "AE_PROB_004",
193
462
  CVAR: "AE_PROB_005"
194
463
  },
195
- // Grover Search
196
464
  GROVER: {
197
465
  BOOLEAN_SAT: "GROVER_BOOL_001",
198
466
  PORTFOLIO_CONSTRAINT: "GROVER_BOOL_002",
@@ -200,7 +468,6 @@ var ALGORITHMS = {
200
468
  COMPLIANCE: "GROVER_BOOL_004",
201
469
  PATTERN: "GROVER_BOOL_005"
202
470
  },
203
- // QAOA Portfolio
204
471
  QAOA_PORTFOLIO: {
205
472
  MARKOWITZ: "QAOA_PORT_001",
206
473
  RISK_PARITY: "QAOA_PORT_002",
@@ -208,7 +475,6 @@ var ALGORITHMS = {
208
475
  MIN_VARIANCE: "QAOA_PORT_004",
209
476
  BLACK_LITTERMAN: "QAOA_PORT_005"
210
477
  },
211
- // QUBO
212
478
  QUBO: {
213
479
  GENERAL: "QAOA_QUBO_001",
214
480
  MAX_CUT: "QAOA_QUBO_002",
@@ -222,5 +488,7 @@ var ALGORITHMS = {
222
488
  ALGORITHMS,
223
489
  QaaSClient,
224
490
  QaaSError,
225
- qaas
491
+ QaaSRateLimitError,
492
+ QaaSValidationError,
493
+ SIGNATURE_MAX_AGE_MS
226
494
  });