@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.d.mts CHANGED
@@ -1652,12 +1652,20 @@ declare class Thread {
1652
1652
 
1653
1653
  /**
1654
1654
  * ThreadManager manages all threads globally
1655
- * Provides methods to create, retrieve, and delete threads
1655
+ * Provides methods to create, retrieve, and delete threads.
1656
+ * Includes automatic cleanup to prevent unbounded memory growth.
1656
1657
  */
1657
1658
  declare class ThreadManager {
1658
1659
  private static instance;
1659
1660
  private threads;
1661
+ private cleanupInterval;
1662
+ private readonly threadTtlMs;
1660
1663
  private constructor();
1664
+ /**
1665
+ * Periodically remove threads older than 7 days.
1666
+ * Runs every hour to avoid frequent iteration over the map.
1667
+ */
1668
+ private startCleanup;
1661
1669
  /**
1662
1670
  * Get singleton instance of ThreadManager
1663
1671
  */
@@ -2449,18 +2457,25 @@ declare class OpenAILLM extends BaseLLM {
2449
2457
  declare const openaiLLM: OpenAILLM;
2450
2458
 
2451
2459
  /**
2452
- * Query Cache - Two separate stores with different lifetimes:
2460
+ * Query Cache Two mechanisms:
2453
2461
  *
2454
- * 1. `cache` (query string → result data) — TTL-based, for avoiding re-execution of recently validated queries.
2455
- * 2. `queryIdCache` (queryId SQL) permanent (server lifetime), because queryIds are persisted
2456
- * in dashboard widgets and must remain resolvable across page reloads and idle periods.
2457
- * Only the cached *data* inside a queryId entry expires; the SQL mapping itself never does.
2462
+ * 1. `cache` (query string → result data) — TTL-based with max size, for avoiding re-execution
2463
+ * of recently validated queries. LRU eviction when max size exceeded.
2464
+ *
2465
+ * 2. Encrypted queryId tokens SQL is encrypted into the queryId itself (self-contained).
2466
+ * No server-side storage needed for SQL mappings. The token is decrypted on each request.
2467
+ * This eliminates the unbounded queryIdCache that previously grew forever and caused
2468
+ * memory bloat (hundreds of MBs after thousands of queries).
2469
+ *
2470
+ * Result data can still be cached temporarily via the data cache (mechanism 1).
2458
2471
  */
2459
2472
  declare class QueryCache {
2460
2473
  private cache;
2461
- private queryIdCache;
2462
2474
  private ttlMs;
2475
+ private maxCacheSize;
2463
2476
  private cleanupInterval;
2477
+ private readonly algorithm;
2478
+ private encryptionKey;
2464
2479
  constructor();
2465
2480
  /**
2466
2481
  * Set the cache TTL (Time To Live)
@@ -2472,8 +2487,7 @@ declare class QueryCache {
2472
2487
  */
2473
2488
  getTTL(): number;
2474
2489
  /**
2475
- * Store query result in cache
2476
- * Key is the exact query string (or JSON for parameterized queries)
2490
+ * Store query result in data cache
2477
2491
  */
2478
2492
  set(query: string, data: any): void;
2479
2493
  /**
@@ -2501,31 +2515,33 @@ declare class QueryCache {
2501
2515
  oldestEntryAge: number | null;
2502
2516
  };
2503
2517
  /**
2504
- * Start periodic cleanup of expired entries.
2505
- * Only cleans the data cache — queryIdCache entries are permanent (server lifetime)
2506
- * because queryIds are persisted in dashboard widgets.
2518
+ * Start periodic cleanup of expired data cache entries.
2507
2519
  */
2508
2520
  private startCleanup;
2509
2521
  /**
2510
- * Generate a unique query ID
2522
+ * Encrypt a payload into a self-contained token.
2523
+ */
2524
+ private encrypt;
2525
+ /**
2526
+ * Decrypt a token back to the original payload.
2511
2527
  */
2512
- private generateQueryId;
2528
+ private decrypt;
2513
2529
  /**
2514
- * Store a query by ID. Returns the generated queryId.
2515
- * The query is stored server-side; only the queryId is sent to the frontend.
2530
+ * Store a query by generating an encrypted token as queryId.
2531
+ * The SQL is encrypted INTO the token nothing stored in memory.
2532
+ * If data is provided, it's cached temporarily in the data cache.
2516
2533
  */
2517
2534
  storeQuery(query: any, data?: any): string;
2518
2535
  /**
2519
- * Get a stored query by its ID.
2520
- * The SQL mapping never expires (queryIds are persisted in dashboard widgets).
2521
- * Cached data may be null if it was evicted by the cleanup interval.
2536
+ * Get a stored query by decrypting its token.
2537
+ * Returns the SQL + any cached result data.
2522
2538
  */
2523
2539
  getQuery(queryId: string): {
2524
2540
  query: any;
2525
2541
  data: any;
2526
2542
  } | null;
2527
2543
  /**
2528
- * Update cached data for a queryId
2544
+ * Update cached data for a queryId token
2529
2545
  */
2530
2546
  setQueryData(queryId: string, data: any): void;
2531
2547
  /**
package/dist/index.d.ts CHANGED
@@ -1652,12 +1652,20 @@ declare class Thread {
1652
1652
 
1653
1653
  /**
1654
1654
  * ThreadManager manages all threads globally
1655
- * Provides methods to create, retrieve, and delete threads
1655
+ * Provides methods to create, retrieve, and delete threads.
1656
+ * Includes automatic cleanup to prevent unbounded memory growth.
1656
1657
  */
1657
1658
  declare class ThreadManager {
1658
1659
  private static instance;
1659
1660
  private threads;
1661
+ private cleanupInterval;
1662
+ private readonly threadTtlMs;
1660
1663
  private constructor();
1664
+ /**
1665
+ * Periodically remove threads older than 7 days.
1666
+ * Runs every hour to avoid frequent iteration over the map.
1667
+ */
1668
+ private startCleanup;
1661
1669
  /**
1662
1670
  * Get singleton instance of ThreadManager
1663
1671
  */
@@ -2449,18 +2457,25 @@ declare class OpenAILLM extends BaseLLM {
2449
2457
  declare const openaiLLM: OpenAILLM;
2450
2458
 
2451
2459
  /**
2452
- * Query Cache - Two separate stores with different lifetimes:
2460
+ * Query Cache Two mechanisms:
2453
2461
  *
2454
- * 1. `cache` (query string → result data) — TTL-based, for avoiding re-execution of recently validated queries.
2455
- * 2. `queryIdCache` (queryId SQL) permanent (server lifetime), because queryIds are persisted
2456
- * in dashboard widgets and must remain resolvable across page reloads and idle periods.
2457
- * Only the cached *data* inside a queryId entry expires; the SQL mapping itself never does.
2462
+ * 1. `cache` (query string → result data) — TTL-based with max size, for avoiding re-execution
2463
+ * of recently validated queries. LRU eviction when max size exceeded.
2464
+ *
2465
+ * 2. Encrypted queryId tokens SQL is encrypted into the queryId itself (self-contained).
2466
+ * No server-side storage needed for SQL mappings. The token is decrypted on each request.
2467
+ * This eliminates the unbounded queryIdCache that previously grew forever and caused
2468
+ * memory bloat (hundreds of MBs after thousands of queries).
2469
+ *
2470
+ * Result data can still be cached temporarily via the data cache (mechanism 1).
2458
2471
  */
2459
2472
  declare class QueryCache {
2460
2473
  private cache;
2461
- private queryIdCache;
2462
2474
  private ttlMs;
2475
+ private maxCacheSize;
2463
2476
  private cleanupInterval;
2477
+ private readonly algorithm;
2478
+ private encryptionKey;
2464
2479
  constructor();
2465
2480
  /**
2466
2481
  * Set the cache TTL (Time To Live)
@@ -2472,8 +2487,7 @@ declare class QueryCache {
2472
2487
  */
2473
2488
  getTTL(): number;
2474
2489
  /**
2475
- * Store query result in cache
2476
- * Key is the exact query string (or JSON for parameterized queries)
2490
+ * Store query result in data cache
2477
2491
  */
2478
2492
  set(query: string, data: any): void;
2479
2493
  /**
@@ -2501,31 +2515,33 @@ declare class QueryCache {
2501
2515
  oldestEntryAge: number | null;
2502
2516
  };
2503
2517
  /**
2504
- * Start periodic cleanup of expired entries.
2505
- * Only cleans the data cache — queryIdCache entries are permanent (server lifetime)
2506
- * because queryIds are persisted in dashboard widgets.
2518
+ * Start periodic cleanup of expired data cache entries.
2507
2519
  */
2508
2520
  private startCleanup;
2509
2521
  /**
2510
- * Generate a unique query ID
2522
+ * Encrypt a payload into a self-contained token.
2523
+ */
2524
+ private encrypt;
2525
+ /**
2526
+ * Decrypt a token back to the original payload.
2511
2527
  */
2512
- private generateQueryId;
2528
+ private decrypt;
2513
2529
  /**
2514
- * Store a query by ID. Returns the generated queryId.
2515
- * The query is stored server-side; only the queryId is sent to the frontend.
2530
+ * Store a query by generating an encrypted token as queryId.
2531
+ * The SQL is encrypted INTO the token nothing stored in memory.
2532
+ * If data is provided, it's cached temporarily in the data cache.
2516
2533
  */
2517
2534
  storeQuery(query: any, data?: any): string;
2518
2535
  /**
2519
- * Get a stored query by its ID.
2520
- * The SQL mapping never expires (queryIds are persisted in dashboard widgets).
2521
- * Cached data may be null if it was evicted by the cleanup interval.
2536
+ * Get a stored query by decrypting its token.
2537
+ * Returns the SQL + any cached result data.
2522
2538
  */
2523
2539
  getQuery(queryId: string): {
2524
2540
  query: any;
2525
2541
  data: any;
2526
2542
  } | null;
2527
2543
  /**
2528
- * Update cached data for a queryId
2544
+ * Update cached data for a queryId token
2529
2545
  */
2530
2546
  setQueryData(queryId: string, data: any): void;
2531
2547
  /**
package/dist/index.js CHANGED
@@ -1400,7 +1400,30 @@ var Thread = class {
1400
1400
  // src/threads/thread-manager.ts
1401
1401
  var ThreadManager = class _ThreadManager {
1402
1402
  constructor() {
1403
+ this.cleanupInterval = null;
1404
+ // Threads older than 7 days are cleaned up
1405
+ this.threadTtlMs = 7 * 24 * 60 * 60 * 1e3;
1403
1406
  this.threads = /* @__PURE__ */ new Map();
1407
+ this.startCleanup();
1408
+ }
1409
+ /**
1410
+ * Periodically remove threads older than 7 days.
1411
+ * Runs every hour to avoid frequent iteration over the map.
1412
+ */
1413
+ startCleanup() {
1414
+ this.cleanupInterval = setInterval(() => {
1415
+ const now = Date.now();
1416
+ let removedCount = 0;
1417
+ for (const [id, thread] of this.threads.entries()) {
1418
+ if (now - thread.getCreatedAt().getTime() > this.threadTtlMs) {
1419
+ this.threads.delete(id);
1420
+ removedCount++;
1421
+ }
1422
+ }
1423
+ if (removedCount > 0) {
1424
+ console.log(`[ThreadManager] Cleaned up ${removedCount} threads older than 7 days (${this.threads.size} remaining)`);
1425
+ }
1426
+ }, 24 * 60 * 60 * 1e3);
1404
1427
  }
1405
1428
  /**
1406
1429
  * Get singleton instance of ThreadManager
@@ -1489,15 +1512,24 @@ var ThreadManager = class _ThreadManager {
1489
1512
  };
1490
1513
 
1491
1514
  // src/utils/query-cache.ts
1515
+ var import_crypto3 = __toESM(require("crypto"));
1492
1516
  var QueryCache = class {
1493
1517
  constructor() {
1494
1518
  this.cache = /* @__PURE__ */ new Map();
1495
- this.queryIdCache = /* @__PURE__ */ new Map();
1496
1519
  this.ttlMs = 10 * 60 * 1e3;
1497
- // Default: 10 minutes (for data cache only)
1520
+ // Default: 10 minutes
1521
+ this.maxCacheSize = 500;
1522
+ // Max data cache entries
1498
1523
  this.cleanupInterval = null;
1524
+ // Encryption for queryId tokens
1525
+ this.algorithm = "aes-256-gcm";
1526
+ const keySource = process.env.QUERY_TOKEN_SECRET || "superatom-query-cache-default-key-v1";
1527
+ this.encryptionKey = import_crypto3.default.createHash("sha256").update(keySource).digest();
1499
1528
  this.startCleanup();
1500
1529
  }
1530
+ // ============================================
1531
+ // Data Cache (TTL-based, max size)
1532
+ // ============================================
1501
1533
  /**
1502
1534
  * Set the cache TTL (Time To Live)
1503
1535
  * @param minutes - TTL in minutes (default: 10)
@@ -1513,10 +1545,13 @@ var QueryCache = class {
1513
1545
  return this.ttlMs / 60 / 1e3;
1514
1546
  }
1515
1547
  /**
1516
- * Store query result in cache
1517
- * Key is the exact query string (or JSON for parameterized queries)
1548
+ * Store query result in data cache
1518
1549
  */
1519
1550
  set(query, data) {
1551
+ if (this.cache.size >= this.maxCacheSize) {
1552
+ const oldestKey = this.cache.keys().next().value;
1553
+ if (oldestKey) this.cache.delete(oldestKey);
1554
+ }
1520
1555
  this.cache.set(query, {
1521
1556
  query,
1522
1557
  data,
@@ -1529,12 +1564,9 @@ var QueryCache = class {
1529
1564
  */
1530
1565
  get(query) {
1531
1566
  const entry = this.cache.get(query);
1532
- if (!entry) {
1533
- return null;
1534
- }
1567
+ if (!entry) return null;
1535
1568
  if (Date.now() - entry.timestamp > this.ttlMs) {
1536
1569
  this.cache.delete(query);
1537
- logger.debug(`[QueryCache] Entry expired for query (${query.substring(0, 50)}...)`);
1538
1570
  return null;
1539
1571
  }
1540
1572
  logger.info(`[QueryCache] Cache HIT for query (${query.substring(0, 50)}...)`);
@@ -1571,14 +1603,13 @@ var QueryCache = class {
1571
1603
  }
1572
1604
  return {
1573
1605
  size: this.cache.size,
1574
- queryIdCount: this.queryIdCache.size,
1606
+ queryIdCount: 0,
1607
+ // No longer stored in memory
1575
1608
  oldestEntryAge: oldestTimestamp ? Date.now() - oldestTimestamp : null
1576
1609
  };
1577
1610
  }
1578
1611
  /**
1579
- * Start periodic cleanup of expired entries.
1580
- * Only cleans the data cache — queryIdCache entries are permanent (server lifetime)
1581
- * because queryIds are persisted in dashboard widgets.
1612
+ * Start periodic cleanup of expired data cache entries.
1582
1613
  */
1583
1614
  startCleanup() {
1584
1615
  this.cleanupInterval = setInterval(() => {
@@ -1590,61 +1621,81 @@ var QueryCache = class {
1590
1621
  expiredCount++;
1591
1622
  }
1592
1623
  }
1593
- for (const entry of this.queryIdCache.values()) {
1594
- if (entry.data && now - entry.timestamp > this.ttlMs) {
1595
- entry.data = null;
1596
- }
1597
- }
1598
1624
  if (expiredCount > 0) {
1599
- logger.debug(`[QueryCache] Cleaned up ${expiredCount} expired data-cache entries`);
1625
+ logger.debug(`[QueryCache] Cleaned up ${expiredCount} expired data-cache entries (${this.cache.size} remaining)`);
1600
1626
  }
1601
1627
  }, 2 * 60 * 1e3);
1602
1628
  }
1603
1629
  // ============================================
1604
- // Query ID Store — maps queryId → query (no SQL sent to frontend)
1630
+ // Encrypted Query ID Tokens (zero memory)
1605
1631
  // ============================================
1606
1632
  /**
1607
- * Generate a unique query ID
1633
+ * Encrypt a payload into a self-contained token.
1608
1634
  */
1609
- generateQueryId() {
1610
- return `qry_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 8)}`;
1635
+ encrypt(payload) {
1636
+ const iv = import_crypto3.default.randomBytes(12);
1637
+ const cipher = import_crypto3.default.createCipheriv(this.algorithm, this.encryptionKey, iv);
1638
+ let encrypted = cipher.update(payload, "utf8", "base64");
1639
+ encrypted += cipher.final("base64");
1640
+ const authTag = cipher.getAuthTag();
1641
+ return iv.toString("base64") + "." + authTag.toString("base64") + "." + encrypted;
1611
1642
  }
1612
1643
  /**
1613
- * Store a query by ID. Returns the generated queryId.
1614
- * The query is stored server-side; only the queryId is sent to the frontend.
1644
+ * Decrypt a token back to the original payload.
1645
+ */
1646
+ decrypt(token) {
1647
+ try {
1648
+ const parts = token.split(".");
1649
+ if (parts.length !== 3) return null;
1650
+ const iv = Buffer.from(parts[0], "base64");
1651
+ const authTag = Buffer.from(parts[1], "base64");
1652
+ const encrypted = parts[2];
1653
+ const decipher = import_crypto3.default.createDecipheriv(this.algorithm, this.encryptionKey, iv);
1654
+ decipher.setAuthTag(authTag);
1655
+ let decrypted = decipher.update(encrypted, "base64", "utf8");
1656
+ decrypted += decipher.final("utf8");
1657
+ return decrypted;
1658
+ } catch (err) {
1659
+ logger.warn(`[QueryCache] Failed to decrypt queryId token: ${err instanceof Error ? err.message : String(err)}`);
1660
+ return null;
1661
+ }
1662
+ }
1663
+ /**
1664
+ * Store a query by generating an encrypted token as queryId.
1665
+ * The SQL is encrypted INTO the token — nothing stored in memory.
1666
+ * If data is provided, it's cached temporarily in the data cache.
1615
1667
  */
1616
1668
  storeQuery(query, data) {
1617
- const queryId = this.generateQueryId();
1618
- this.queryIdCache.set(queryId, {
1619
- queryId,
1620
- query,
1621
- data: data || null,
1622
- timestamp: Date.now()
1623
- });
1624
- const queryPreview = typeof query === "string" ? query.substring(0, 50) : JSON.stringify(query).substring(0, 50);
1625
- logger.debug(`[QueryCache] Stored query as ${queryId} (${queryPreview}...)`);
1669
+ const payload = typeof query === "string" ? query : JSON.stringify(query);
1670
+ const queryId = this.encrypt(payload);
1671
+ if (data) {
1672
+ this.set(queryId, data);
1673
+ }
1674
+ const queryPreview = payload.substring(0, 50);
1675
+ logger.debug(`[QueryCache] Stored query as encrypted token (${queryPreview}...)`);
1626
1676
  return queryId;
1627
1677
  }
1628
1678
  /**
1629
- * Get a stored query by its ID.
1630
- * The SQL mapping never expires (queryIds are persisted in dashboard widgets).
1631
- * Cached data may be null if it was evicted by the cleanup interval.
1679
+ * Get a stored query by decrypting its token.
1680
+ * Returns the SQL + any cached result data.
1632
1681
  */
1633
1682
  getQuery(queryId) {
1634
- const entry = this.queryIdCache.get(queryId);
1635
- if (!entry) return null;
1636
- entry.timestamp = Date.now();
1637
- return { query: entry.query, data: entry.data };
1683
+ const decrypted = this.decrypt(queryId);
1684
+ if (!decrypted) return null;
1685
+ let query;
1686
+ try {
1687
+ query = JSON.parse(decrypted);
1688
+ } catch {
1689
+ query = decrypted;
1690
+ }
1691
+ const cachedData = this.get(queryId);
1692
+ return { query, data: cachedData };
1638
1693
  }
1639
1694
  /**
1640
- * Update cached data for a queryId
1695
+ * Update cached data for a queryId token
1641
1696
  */
1642
1697
  setQueryData(queryId, data) {
1643
- const entry = this.queryIdCache.get(queryId);
1644
- if (entry) {
1645
- entry.data = data;
1646
- entry.timestamp = Date.now();
1647
- }
1698
+ this.set(queryId, data);
1648
1699
  }
1649
1700
  /**
1650
1701
  * Stop cleanup interval (for graceful shutdown)
@@ -1655,7 +1706,6 @@ var QueryCache = class {
1655
1706
  this.cleanupInterval = null;
1656
1707
  }
1657
1708
  this.cache.clear();
1658
- this.queryIdCache.clear();
1659
1709
  }
1660
1710
  };
1661
1711
  var queryCache = new QueryCache();
@@ -2258,7 +2308,7 @@ async function handleBundleRequest(data, bundleDir, sendMessage) {
2258
2308
  }
2259
2309
 
2260
2310
  // src/auth/utils.ts
2261
- var import_crypto3 = __toESM(require("crypto"));
2311
+ var import_crypto4 = __toESM(require("crypto"));
2262
2312
  function decodeBase64ToJson(base64Data) {
2263
2313
  try {
2264
2314
  const decodedString = Buffer.from(base64Data, "base64").toString("utf-8");
@@ -2268,7 +2318,7 @@ function decodeBase64ToJson(base64Data) {
2268
2318
  }
2269
2319
  }
2270
2320
  function hashPassword(password) {
2271
- return import_crypto3.default.createHash("sha1").update(password).digest("hex");
2321
+ return import_crypto4.default.createHash("sha1").update(password).digest("hex");
2272
2322
  }
2273
2323
 
2274
2324
  // src/auth/user-storage.ts