@superatomai/sdk-node 0.0.28-mds → 0.0.29-mds

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.mjs CHANGED
@@ -1340,7 +1340,30 @@ var Thread = class {
1340
1340
  // src/threads/thread-manager.ts
1341
1341
  var ThreadManager = class _ThreadManager {
1342
1342
  constructor() {
1343
+ this.cleanupInterval = null;
1344
+ // Threads older than 7 days are cleaned up
1345
+ this.threadTtlMs = 7 * 24 * 60 * 60 * 1e3;
1343
1346
  this.threads = /* @__PURE__ */ new Map();
1347
+ this.startCleanup();
1348
+ }
1349
+ /**
1350
+ * Periodically remove threads older than 7 days.
1351
+ * Runs every hour to avoid frequent iteration over the map.
1352
+ */
1353
+ startCleanup() {
1354
+ this.cleanupInterval = setInterval(() => {
1355
+ const now = Date.now();
1356
+ let removedCount = 0;
1357
+ for (const [id, thread] of this.threads.entries()) {
1358
+ if (now - thread.getCreatedAt().getTime() > this.threadTtlMs) {
1359
+ this.threads.delete(id);
1360
+ removedCount++;
1361
+ }
1362
+ }
1363
+ if (removedCount > 0) {
1364
+ console.log(`[ThreadManager] Cleaned up ${removedCount} threads older than 7 days (${this.threads.size} remaining)`);
1365
+ }
1366
+ }, 24 * 60 * 60 * 1e3);
1344
1367
  }
1345
1368
  /**
1346
1369
  * Get singleton instance of ThreadManager
@@ -1429,15 +1452,24 @@ var ThreadManager = class _ThreadManager {
1429
1452
  };
1430
1453
 
1431
1454
  // src/utils/query-cache.ts
1455
+ import crypto from "crypto";
1432
1456
  var QueryCache = class {
1433
1457
  constructor() {
1434
1458
  this.cache = /* @__PURE__ */ new Map();
1435
- this.queryIdCache = /* @__PURE__ */ new Map();
1436
1459
  this.ttlMs = 10 * 60 * 1e3;
1437
- // Default: 10 minutes (for data cache only)
1460
+ // Default: 10 minutes
1461
+ this.maxCacheSize = 500;
1462
+ // Max data cache entries
1438
1463
  this.cleanupInterval = null;
1464
+ // Encryption for queryId tokens
1465
+ this.algorithm = "aes-256-gcm";
1466
+ const keySource = process.env.QUERY_TOKEN_SECRET || "superatom-query-cache-default-key-v1";
1467
+ this.encryptionKey = crypto.createHash("sha256").update(keySource).digest();
1439
1468
  this.startCleanup();
1440
1469
  }
1470
+ // ============================================
1471
+ // Data Cache (TTL-based, max size)
1472
+ // ============================================
1441
1473
  /**
1442
1474
  * Set the cache TTL (Time To Live)
1443
1475
  * @param minutes - TTL in minutes (default: 10)
@@ -1453,10 +1485,13 @@ var QueryCache = class {
1453
1485
  return this.ttlMs / 60 / 1e3;
1454
1486
  }
1455
1487
  /**
1456
- * Store query result in cache
1457
- * Key is the exact query string (or JSON for parameterized queries)
1488
+ * Store query result in data cache
1458
1489
  */
1459
1490
  set(query, data) {
1491
+ if (this.cache.size >= this.maxCacheSize) {
1492
+ const oldestKey = this.cache.keys().next().value;
1493
+ if (oldestKey) this.cache.delete(oldestKey);
1494
+ }
1460
1495
  this.cache.set(query, {
1461
1496
  query,
1462
1497
  data,
@@ -1469,12 +1504,9 @@ var QueryCache = class {
1469
1504
  */
1470
1505
  get(query) {
1471
1506
  const entry = this.cache.get(query);
1472
- if (!entry) {
1473
- return null;
1474
- }
1507
+ if (!entry) return null;
1475
1508
  if (Date.now() - entry.timestamp > this.ttlMs) {
1476
1509
  this.cache.delete(query);
1477
- logger.debug(`[QueryCache] Entry expired for query (${query.substring(0, 50)}...)`);
1478
1510
  return null;
1479
1511
  }
1480
1512
  logger.info(`[QueryCache] Cache HIT for query (${query.substring(0, 50)}...)`);
@@ -1511,14 +1543,13 @@ var QueryCache = class {
1511
1543
  }
1512
1544
  return {
1513
1545
  size: this.cache.size,
1514
- queryIdCount: this.queryIdCache.size,
1546
+ queryIdCount: 0,
1547
+ // No longer stored in memory
1515
1548
  oldestEntryAge: oldestTimestamp ? Date.now() - oldestTimestamp : null
1516
1549
  };
1517
1550
  }
1518
1551
  /**
1519
- * Start periodic cleanup of expired entries.
1520
- * Only cleans the data cache — queryIdCache entries are permanent (server lifetime)
1521
- * because queryIds are persisted in dashboard widgets.
1552
+ * Start periodic cleanup of expired data cache entries.
1522
1553
  */
1523
1554
  startCleanup() {
1524
1555
  this.cleanupInterval = setInterval(() => {
@@ -1530,61 +1561,81 @@ var QueryCache = class {
1530
1561
  expiredCount++;
1531
1562
  }
1532
1563
  }
1533
- for (const entry of this.queryIdCache.values()) {
1534
- if (entry.data && now - entry.timestamp > this.ttlMs) {
1535
- entry.data = null;
1536
- }
1537
- }
1538
1564
  if (expiredCount > 0) {
1539
- logger.debug(`[QueryCache] Cleaned up ${expiredCount} expired data-cache entries`);
1565
+ logger.debug(`[QueryCache] Cleaned up ${expiredCount} expired data-cache entries (${this.cache.size} remaining)`);
1540
1566
  }
1541
1567
  }, 2 * 60 * 1e3);
1542
1568
  }
1543
1569
  // ============================================
1544
- // Query ID Store — maps queryId → query (no SQL sent to frontend)
1570
+ // Encrypted Query ID Tokens (zero memory)
1545
1571
  // ============================================
1546
1572
  /**
1547
- * Generate a unique query ID
1573
+ * Encrypt a payload into a self-contained token.
1548
1574
  */
1549
- generateQueryId() {
1550
- return `qry_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 8)}`;
1575
+ encrypt(payload) {
1576
+ const iv = crypto.randomBytes(12);
1577
+ const cipher = crypto.createCipheriv(this.algorithm, this.encryptionKey, iv);
1578
+ let encrypted = cipher.update(payload, "utf8", "base64");
1579
+ encrypted += cipher.final("base64");
1580
+ const authTag = cipher.getAuthTag();
1581
+ return iv.toString("base64") + "." + authTag.toString("base64") + "." + encrypted;
1551
1582
  }
1552
1583
  /**
1553
- * Store a query by ID. Returns the generated queryId.
1554
- * The query is stored server-side; only the queryId is sent to the frontend.
1584
+ * Decrypt a token back to the original payload.
1585
+ */
1586
+ decrypt(token) {
1587
+ try {
1588
+ const parts = token.split(".");
1589
+ if (parts.length !== 3) return null;
1590
+ const iv = Buffer.from(parts[0], "base64");
1591
+ const authTag = Buffer.from(parts[1], "base64");
1592
+ const encrypted = parts[2];
1593
+ const decipher = crypto.createDecipheriv(this.algorithm, this.encryptionKey, iv);
1594
+ decipher.setAuthTag(authTag);
1595
+ let decrypted = decipher.update(encrypted, "base64", "utf8");
1596
+ decrypted += decipher.final("utf8");
1597
+ return decrypted;
1598
+ } catch (err) {
1599
+ logger.warn(`[QueryCache] Failed to decrypt queryId token: ${err instanceof Error ? err.message : String(err)}`);
1600
+ return null;
1601
+ }
1602
+ }
1603
+ /**
1604
+ * Store a query by generating an encrypted token as queryId.
1605
+ * The SQL is encrypted INTO the token — nothing stored in memory.
1606
+ * If data is provided, it's cached temporarily in the data cache.
1555
1607
  */
1556
1608
  storeQuery(query, data) {
1557
- const queryId = this.generateQueryId();
1558
- this.queryIdCache.set(queryId, {
1559
- queryId,
1560
- query,
1561
- data: data || null,
1562
- timestamp: Date.now()
1563
- });
1564
- const queryPreview = typeof query === "string" ? query.substring(0, 50) : JSON.stringify(query).substring(0, 50);
1565
- logger.debug(`[QueryCache] Stored query as ${queryId} (${queryPreview}...)`);
1609
+ const payload = typeof query === "string" ? query : JSON.stringify(query);
1610
+ const queryId = this.encrypt(payload);
1611
+ if (data) {
1612
+ this.set(queryId, data);
1613
+ }
1614
+ const queryPreview = payload.substring(0, 50);
1615
+ logger.debug(`[QueryCache] Stored query as encrypted token (${queryPreview}...)`);
1566
1616
  return queryId;
1567
1617
  }
1568
1618
  /**
1569
- * Get a stored query by its ID.
1570
- * The SQL mapping never expires (queryIds are persisted in dashboard widgets).
1571
- * Cached data may be null if it was evicted by the cleanup interval.
1619
+ * Get a stored query by decrypting its token.
1620
+ * Returns the SQL + any cached result data.
1572
1621
  */
1573
1622
  getQuery(queryId) {
1574
- const entry = this.queryIdCache.get(queryId);
1575
- if (!entry) return null;
1576
- entry.timestamp = Date.now();
1577
- return { query: entry.query, data: entry.data };
1623
+ const decrypted = this.decrypt(queryId);
1624
+ if (!decrypted) return null;
1625
+ let query;
1626
+ try {
1627
+ query = JSON.parse(decrypted);
1628
+ } catch {
1629
+ query = decrypted;
1630
+ }
1631
+ const cachedData = this.get(queryId);
1632
+ return { query, data: cachedData };
1578
1633
  }
1579
1634
  /**
1580
- * Update cached data for a queryId
1635
+ * Update cached data for a queryId token
1581
1636
  */
1582
1637
  setQueryData(queryId, data) {
1583
- const entry = this.queryIdCache.get(queryId);
1584
- if (entry) {
1585
- entry.data = data;
1586
- entry.timestamp = Date.now();
1587
- }
1638
+ this.set(queryId, data);
1588
1639
  }
1589
1640
  /**
1590
1641
  * Stop cleanup interval (for graceful shutdown)
@@ -1595,7 +1646,6 @@ var QueryCache = class {
1595
1646
  this.cleanupInterval = null;
1596
1647
  }
1597
1648
  this.cache.clear();
1598
- this.queryIdCache.clear();
1599
1649
  }
1600
1650
  };
1601
1651
  var queryCache = new QueryCache();
@@ -2198,7 +2248,7 @@ async function handleBundleRequest(data, bundleDir, sendMessage) {
2198
2248
  }
2199
2249
 
2200
2250
  // src/auth/utils.ts
2201
- import crypto from "crypto";
2251
+ import crypto2 from "crypto";
2202
2252
  function decodeBase64ToJson(base64Data) {
2203
2253
  try {
2204
2254
  const decodedString = Buffer.from(base64Data, "base64").toString("utf-8");
@@ -2208,7 +2258,7 @@ function decodeBase64ToJson(base64Data) {
2208
2258
  }
2209
2259
  }
2210
2260
  function hashPassword(password) {
2211
- return crypto.createHash("sha1").update(password).digest("hex");
2261
+ return crypto2.createHash("sha1").update(password).digest("hex");
2212
2262
  }
2213
2263
 
2214
2264
  // src/auth/user-storage.ts