@superatomai/sdk-node 0.0.26 → 0.0.28

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
@@ -587,6 +587,7 @@ type CollectionOperation = 'getMany' | 'getOne' | 'query' | 'mutation' | 'update
587
587
  type CollectionHandler<TParams = any, TResult = any> = (params: TParams) => Promise<TResult> | TResult;
588
588
  type LLMProvider = 'anthropic' | 'groq' | 'gemini' | 'openai';
589
589
 
590
+ type DatabaseType = 'postgresql' | 'mssql';
590
591
  interface SuperatomSDKConfig {
591
592
  url?: string;
592
593
  apiKey?: string;
@@ -595,6 +596,7 @@ interface SuperatomSDKConfig {
595
596
  type?: string;
596
597
  bundleDir?: string;
597
598
  promptsDir?: string;
599
+ databaseType?: DatabaseType;
598
600
  ANTHROPIC_API_KEY?: string;
599
601
  GROQ_API_KEY?: string;
600
602
  GEMINI_API_KEY?: string;
@@ -1466,15 +1468,17 @@ declare const STORAGE_CONFIG: {
1466
1468
  */
1467
1469
  MAX_ROWS_PER_BLOCK: number;
1468
1470
  /**
1469
- * Maximum size in bytes per UIBlock (1MB)
1471
+ * Maximum size in bytes per UIBlock (500KB - reduced to save memory)
1470
1472
  */
1471
1473
  MAX_SIZE_PER_BLOCK_BYTES: number;
1472
1474
  /**
1473
1475
  * Number of days to keep threads before cleanup
1476
+ * Note: This is for in-memory storage. Conversations are also persisted to database.
1474
1477
  */
1475
1478
  THREAD_RETENTION_DAYS: number;
1476
1479
  /**
1477
1480
  * Number of days to keep UIBlocks before cleanup
1481
+ * Note: This is for in-memory storage. Data is also persisted to database.
1478
1482
  */
1479
1483
  UIBLOCK_RETENTION_DAYS: number;
1480
1484
  };
@@ -1642,6 +1646,7 @@ declare class SuperatomSDK {
1642
1646
  private geminiApiKey;
1643
1647
  private openaiApiKey;
1644
1648
  private llmProviders;
1649
+ private databaseType;
1645
1650
  private userManager;
1646
1651
  private dashboardManager;
1647
1652
  private reportManager;
@@ -1723,4 +1728,4 @@ declare class SuperatomSDK {
1723
1728
  getTools(): Tool$1[];
1724
1729
  }
1725
1730
 
1726
- export { type Action, BM25L, type BM25LOptions, CONTEXT_CONFIG, type CapturedLog, CleanupService, type CollectionHandler, type CollectionOperation, type DBUIBlock, type HybridSearchOptions, type IncomingMessage, type KbNodesQueryFilters, type KbNodesRequestPayload, LLM, type LogLevel, type Message, type RerankedResult, SDK_VERSION, STORAGE_CONFIG, SuperatomSDK, type SuperatomSDKConfig, Thread, ThreadManager, type Tool$1 as Tool, UIBlock, UILogCollector, type User, UserManager, type UsersData, hybridRerank, logger, rerankChromaResults, rerankConversationResults };
1731
+ export { type Action, BM25L, type BM25LOptions, CONTEXT_CONFIG, type CapturedLog, CleanupService, type CollectionHandler, type CollectionOperation, type DBUIBlock, type DatabaseType, type HybridSearchOptions, type IncomingMessage, type KbNodesQueryFilters, type KbNodesRequestPayload, LLM, type LogLevel, type Message, type RerankedResult, SDK_VERSION, STORAGE_CONFIG, SuperatomSDK, type SuperatomSDKConfig, Thread, ThreadManager, type Tool$1 as Tool, UIBlock, UILogCollector, type User, UserManager, type UsersData, hybridRerank, logger, rerankChromaResults, rerankConversationResults };
package/dist/index.d.ts CHANGED
@@ -587,6 +587,7 @@ type CollectionOperation = 'getMany' | 'getOne' | 'query' | 'mutation' | 'update
587
587
  type CollectionHandler<TParams = any, TResult = any> = (params: TParams) => Promise<TResult> | TResult;
588
588
  type LLMProvider = 'anthropic' | 'groq' | 'gemini' | 'openai';
589
589
 
590
+ type DatabaseType = 'postgresql' | 'mssql';
590
591
  interface SuperatomSDKConfig {
591
592
  url?: string;
592
593
  apiKey?: string;
@@ -595,6 +596,7 @@ interface SuperatomSDKConfig {
595
596
  type?: string;
596
597
  bundleDir?: string;
597
598
  promptsDir?: string;
599
+ databaseType?: DatabaseType;
598
600
  ANTHROPIC_API_KEY?: string;
599
601
  GROQ_API_KEY?: string;
600
602
  GEMINI_API_KEY?: string;
@@ -1466,15 +1468,17 @@ declare const STORAGE_CONFIG: {
1466
1468
  */
1467
1469
  MAX_ROWS_PER_BLOCK: number;
1468
1470
  /**
1469
- * Maximum size in bytes per UIBlock (1MB)
1471
+ * Maximum size in bytes per UIBlock (500KB - reduced to save memory)
1470
1472
  */
1471
1473
  MAX_SIZE_PER_BLOCK_BYTES: number;
1472
1474
  /**
1473
1475
  * Number of days to keep threads before cleanup
1476
+ * Note: This is for in-memory storage. Conversations are also persisted to database.
1474
1477
  */
1475
1478
  THREAD_RETENTION_DAYS: number;
1476
1479
  /**
1477
1480
  * Number of days to keep UIBlocks before cleanup
1481
+ * Note: This is for in-memory storage. Data is also persisted to database.
1478
1482
  */
1479
1483
  UIBLOCK_RETENTION_DAYS: number;
1480
1484
  };
@@ -1642,6 +1646,7 @@ declare class SuperatomSDK {
1642
1646
  private geminiApiKey;
1643
1647
  private openaiApiKey;
1644
1648
  private llmProviders;
1649
+ private databaseType;
1645
1650
  private userManager;
1646
1651
  private dashboardManager;
1647
1652
  private reportManager;
@@ -1723,4 +1728,4 @@ declare class SuperatomSDK {
1723
1728
  getTools(): Tool$1[];
1724
1729
  }
1725
1730
 
1726
- export { type Action, BM25L, type BM25LOptions, CONTEXT_CONFIG, type CapturedLog, CleanupService, type CollectionHandler, type CollectionOperation, type DBUIBlock, type HybridSearchOptions, type IncomingMessage, type KbNodesQueryFilters, type KbNodesRequestPayload, LLM, type LogLevel, type Message, type RerankedResult, SDK_VERSION, STORAGE_CONFIG, SuperatomSDK, type SuperatomSDKConfig, Thread, ThreadManager, type Tool$1 as Tool, UIBlock, UILogCollector, type User, UserManager, type UsersData, hybridRerank, logger, rerankChromaResults, rerankConversationResults };
1731
+ export { type Action, BM25L, type BM25LOptions, CONTEXT_CONFIG, type CapturedLog, CleanupService, type CollectionHandler, type CollectionOperation, type DBUIBlock, type DatabaseType, type HybridSearchOptions, type IncomingMessage, type KbNodesQueryFilters, type KbNodesRequestPayload, LLM, type LogLevel, type Message, type RerankedResult, SDK_VERSION, STORAGE_CONFIG, SuperatomSDK, type SuperatomSDKConfig, Thread, ThreadManager, type Tool$1 as Tool, UIBlock, UILogCollector, type User, UserManager, type UsersData, hybridRerank, logger, rerankChromaResults, rerankConversationResults };
package/dist/index.js CHANGED
@@ -846,18 +846,22 @@ var STORAGE_CONFIG = {
846
846
  */
847
847
  MAX_ROWS_PER_BLOCK: 10,
848
848
  /**
849
- * Maximum size in bytes per UIBlock (1MB)
849
+ * Maximum size in bytes per UIBlock (500KB - reduced to save memory)
850
850
  */
851
- MAX_SIZE_PER_BLOCK_BYTES: 1 * 1024 * 1024,
852
- // 1MB
851
+ MAX_SIZE_PER_BLOCK_BYTES: 500 * 1024,
852
+ // 500KB
853
853
  /**
854
854
  * Number of days to keep threads before cleanup
855
+ * Note: This is for in-memory storage. Conversations are also persisted to database.
855
856
  */
856
- THREAD_RETENTION_DAYS: 7,
857
+ THREAD_RETENTION_DAYS: 2,
858
+ // Reduced from 7 to 1 day for memory efficiency
857
859
  /**
858
860
  * Number of days to keep UIBlocks before cleanup
861
+ * Note: This is for in-memory storage. Data is also persisted to database.
859
862
  */
860
- UIBLOCK_RETENTION_DAYS: 7
863
+ UIBLOCK_RETENTION_DAYS: 2
864
+ // Reduced from 7 to 1 day for memory efficiency
861
865
  };
862
866
 
863
867
  // src/threads/uiblock.ts
@@ -1377,8 +1381,15 @@ async function handleDataRequest(data, collections, sendMessage) {
1377
1381
  }
1378
1382
  }
1379
1383
  if (uiBlock) {
1380
- uiBlock.setComponentData(result || {});
1381
- logger.info(`Updated UIBlock ${uiBlockId} with component data from ${collection}.${op}`);
1384
+ const dataSummary = {
1385
+ _dataReceived: true,
1386
+ _timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1387
+ _collection: collection,
1388
+ _operation: op,
1389
+ _recordCount: Array.isArray(result) ? result.length : result?.data?.length || result?.contacts?.length || result?.salesorders?.length || "unknown"
1390
+ };
1391
+ uiBlock.setComponentData(dataSummary);
1392
+ logger.info(`Updated UIBlock ${uiBlockId} with data summary from ${collection}.${op} (full data not stored to save memory)`);
1382
1393
  } else {
1383
1394
  logger.warn(`UIBlock ${uiBlockId} not found in threads`);
1384
1395
  }
@@ -2925,7 +2936,9 @@ If adaptation is not possible or would fundamentally change the component:
2925
2936
  var PromptLoader = class {
2926
2937
  constructor(config) {
2927
2938
  this.promptCache = /* @__PURE__ */ new Map();
2939
+ this.databaseRulesCache = /* @__PURE__ */ new Map();
2928
2940
  this.isInitialized = false;
2941
+ this.databaseType = "postgresql";
2929
2942
  logger.debug("Initializing PromptLoader...");
2930
2943
  this.promptsDir = config?.promptsDir || import_path2.default.join(process.cwd(), ".prompts");
2931
2944
  logger.debug(`Prompts directory set to: ${this.promptsDir}`);
@@ -3071,6 +3084,76 @@ var PromptLoader = class {
3071
3084
  getCacheSize() {
3072
3085
  return this.promptCache.size;
3073
3086
  }
3087
+ /**
3088
+ * Set the database type for SQL rules loading
3089
+ * @param type - Database type ('postgresql' | 'mssql')
3090
+ */
3091
+ setDatabaseType(type) {
3092
+ this.databaseType = type;
3093
+ this.databaseRulesCache.clear();
3094
+ logger.debug(`Database type set to: ${type}`);
3095
+ }
3096
+ /**
3097
+ * Get current database type
3098
+ * @returns Database type string
3099
+ */
3100
+ getDatabaseType() {
3101
+ return this.databaseType;
3102
+ }
3103
+ /**
3104
+ * Load database-specific SQL rules from file system
3105
+ * Falls back to minimal default rules if file not found
3106
+ * @returns Database rules as a string
3107
+ */
3108
+ async loadDatabaseRules() {
3109
+ if (this.databaseRulesCache.has(this.databaseType)) {
3110
+ logger.debug(`\u2713 Database rules for '${this.databaseType}' loaded from cache`);
3111
+ return this.databaseRulesCache.get(this.databaseType);
3112
+ }
3113
+ const rulesPath = import_path2.default.join(this.promptsDir, "database-rules", `${this.databaseType}.md`);
3114
+ try {
3115
+ if (import_fs3.default.existsSync(rulesPath)) {
3116
+ const rules = import_fs3.default.readFileSync(rulesPath, "utf-8");
3117
+ this.databaseRulesCache.set(this.databaseType, rules);
3118
+ logger.info(`\u2713 Loaded database rules for '${this.databaseType}' from ${rulesPath}`);
3119
+ return rules;
3120
+ }
3121
+ } catch (error) {
3122
+ logger.warn(`Could not load database rules for '${this.databaseType}' from file system: ${error}`);
3123
+ }
3124
+ const defaultRules = this.getDefaultDatabaseRules();
3125
+ this.databaseRulesCache.set(this.databaseType, defaultRules);
3126
+ logger.warn(`Using default database rules for '${this.databaseType}' (file not found at ${rulesPath})`);
3127
+ return defaultRules;
3128
+ }
3129
+ /**
3130
+ * Get default database rules as fallback
3131
+ * @returns Minimal database rules
3132
+ */
3133
+ getDefaultDatabaseRules() {
3134
+ if (this.databaseType === "mssql") {
3135
+ return `**Database Type: Microsoft SQL Server**
3136
+
3137
+ **SQL Query Rules:**
3138
+ - Use \`TOP N\` for row limiting (e.g., \`SELECT TOP 32 * FROM table\`)
3139
+ - Use \`1\` for true, \`0\` for false (no native boolean)
3140
+ - Use \`+\` or \`CONCAT()\` for string concatenation
3141
+ - Use \`GETDATE()\` for current timestamp
3142
+ - Use \`CAST()\` or \`CONVERT()\` for type casting
3143
+ - Use \`OUTPUT INSERTED.*\` instead of \`RETURNING\`
3144
+ - NULL values: Use \`NULL\` keyword without quotes`;
3145
+ }
3146
+ return `**Database Type: PostgreSQL**
3147
+
3148
+ **SQL Query Rules:**
3149
+ - Use \`LIMIT N\` for row limiting (e.g., \`SELECT * FROM table LIMIT 32\`)
3150
+ - Use \`true\` / \`false\` for boolean values
3151
+ - Use \`||\` for string concatenation
3152
+ - Use \`NOW()\` for current timestamp
3153
+ - Use \`::TYPE\` or \`CAST()\` for type casting
3154
+ - Use \`RETURNING\` clause for mutations
3155
+ - NULL values: Use \`NULL\` keyword without quotes`;
3156
+ }
3074
3157
  };
3075
3158
  var defaultPromptsPath = process.env.PROMPTS_DIR || import_path2.default.join(process.cwd(), ".prompts");
3076
3159
  var promptLoader = new PromptLoader({
@@ -3385,10 +3468,15 @@ var LLM = class {
3385
3468
  for (const toolUse of toolUses) {
3386
3469
  try {
3387
3470
  const result = await toolHandler(toolUse.name, toolUse.input);
3471
+ let resultContent = typeof result === "string" ? result : JSON.stringify(result);
3472
+ const MAX_RESULT_LENGTH = 5e4;
3473
+ if (resultContent.length > MAX_RESULT_LENGTH) {
3474
+ resultContent = resultContent.substring(0, MAX_RESULT_LENGTH) + "\n\n... [Result truncated - showing first 50000 characters of " + resultContent.length + " total]";
3475
+ }
3388
3476
  toolResults.content.push({
3389
3477
  type: "tool_result",
3390
3478
  tool_use_id: toolUse.id,
3391
- content: typeof result === "string" ? result : JSON.stringify(result)
3479
+ content: resultContent
3392
3480
  });
3393
3481
  } catch (error) {
3394
3482
  toolResults.content.push({
@@ -3567,9 +3655,14 @@ var LLM = class {
3567
3655
  for (const fc of functionCalls) {
3568
3656
  try {
3569
3657
  const result2 = await toolHandler(fc.name, fc.args);
3658
+ let resultContent = typeof result2 === "string" ? result2 : JSON.stringify(result2);
3659
+ const MAX_RESULT_LENGTH = 5e4;
3660
+ if (resultContent.length > MAX_RESULT_LENGTH) {
3661
+ resultContent = resultContent.substring(0, MAX_RESULT_LENGTH) + "\n\n... [Result truncated - showing first 50000 characters of " + resultContent.length + " total]";
3662
+ }
3570
3663
  functionResponses.push({
3571
3664
  name: fc.name,
3572
- response: { result: typeof result2 === "string" ? result2 : JSON.stringify(result2) }
3665
+ response: { result: resultContent }
3573
3666
  });
3574
3667
  } catch (error) {
3575
3668
  functionResponses.push({
@@ -3735,6 +3828,10 @@ var LLM = class {
3735
3828
  const args = JSON.parse(tc.arguments);
3736
3829
  const toolResult = await toolHandler(tc.name, args);
3737
3830
  result = typeof toolResult === "string" ? toolResult : JSON.stringify(toolResult);
3831
+ const MAX_RESULT_LENGTH = 5e4;
3832
+ if (result.length > MAX_RESULT_LENGTH) {
3833
+ result = result.substring(0, MAX_RESULT_LENGTH) + "\n\n... [Result truncated - showing first 50000 characters of " + result.length + " total]";
3834
+ }
3738
3835
  } catch (error) {
3739
3836
  result = JSON.stringify({ error: error instanceof Error ? error.message : String(error) });
3740
3837
  }
@@ -4215,6 +4312,7 @@ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
4215
4312
  }).join("\n\n");
4216
4313
  }
4217
4314
  const schemaDoc = schema.generateSchemaDocumentation();
4315
+ const databaseRules = await promptLoader.loadDatabaseRules();
4218
4316
  logger.file("\n=============================\nText analysis response:", analysisContent);
4219
4317
  logger.file("\n=============================\nDeferred tools:", deferredToolsText);
4220
4318
  logger.file("\n=============================\nExecuted tools:", executedToolsText);
@@ -4222,6 +4320,7 @@ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
4222
4320
  ANALYSIS_CONTENT: analysisContent,
4223
4321
  AVAILABLE_COMPONENTS: availableComponentsText,
4224
4322
  SCHEMA_DOC: schemaDoc,
4323
+ DATABASE_RULES: databaseRules,
4225
4324
  DEFERRED_TOOLS: deferredToolsText,
4226
4325
  EXECUTED_TOOLS: executedToolsText
4227
4326
  });
@@ -4482,12 +4581,14 @@ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
4482
4581
  };
4483
4582
  }
4484
4583
  const schemaDoc = schema.generateSchemaDocumentation();
4584
+ const databaseRules = await promptLoader.loadDatabaseRules();
4485
4585
  const prompts = await promptLoader.loadPrompts("adapt-ui-block-params", {
4486
4586
  ORIGINAL_USER_PROMPT: originalUserPrompt,
4487
4587
  CURRENT_USER_PROMPT: currentUserPrompt,
4488
4588
  MATCHED_UI_BLOCK_COMPONENT: JSON.stringify(component, null, 2),
4489
4589
  COMPONENT_PROPS: JSON.stringify(component.props, null, 2),
4490
- SCHEMA_DOC: schemaDoc || "No schema available"
4590
+ SCHEMA_DOC: schemaDoc || "No schema available",
4591
+ DATABASE_RULES: databaseRules
4491
4592
  });
4492
4593
  const result = await LLM.stream(
4493
4594
  {
@@ -4606,6 +4707,7 @@ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
4606
4707
  availableToolsDoc = toolsDocParts.join("\n\n---\n\n");
4607
4708
  }
4608
4709
  const schemaDoc = schema.generateSchemaDocumentation();
4710
+ const databaseRules = await promptLoader.loadDatabaseRules();
4609
4711
  const knowledgeBaseContext = await knowledge_base_default.getKnowledgeBase({
4610
4712
  prompt: userPrompt,
4611
4713
  collections,
@@ -4616,6 +4718,7 @@ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
4616
4718
  USER_PROMPT: userPrompt,
4617
4719
  CONVERSATION_HISTORY: conversationHistory || "No previous conversation",
4618
4720
  SCHEMA_DOC: schemaDoc,
4721
+ DATABASE_RULES: databaseRules,
4619
4722
  KNOWLEDGE_BASE_CONTEXT: knowledgeBaseContext || "No additional knowledge base context available.",
4620
4723
  AVAILABLE_EXTERNAL_TOOLS: availableToolsDoc
4621
4724
  });
@@ -4909,13 +5012,26 @@ Please try rephrasing your request or contact support.
4909
5012
  logger.info(`[${this.getProviderName()}] External tool ${externalTool.name} executed successfully`);
4910
5013
  logCollector?.info(`\u2713 ${externalTool.name} executed successfully`);
4911
5014
  if (!executedToolsList.find((t) => t.id === externalTool.id)) {
5015
+ let resultSummary = null;
5016
+ if (result2) {
5017
+ const resultStr = typeof result2 === "string" ? result2 : JSON.stringify(result2);
5018
+ if (resultStr.length > 1e3) {
5019
+ resultSummary = {
5020
+ _preview: resultStr.substring(0, 1e3) + "... (truncated)",
5021
+ _totalLength: resultStr.length,
5022
+ _recordCount: Array.isArray(result2) ? result2.length : result2?.data?.length || result2?.contacts?.length || result2?.salesorders?.length || "unknown"
5023
+ };
5024
+ } else {
5025
+ resultSummary = result2;
5026
+ }
5027
+ }
4912
5028
  executedToolsList.push({
4913
5029
  id: externalTool.id,
4914
5030
  name: externalTool.name,
4915
5031
  params: toolInput,
4916
5032
  // The actual parameters used in this execution
4917
- result: result2
4918
- // Store the actual result data for populating deferred tool params
5033
+ result: resultSummary
5034
+ // Store summary instead of full result to save memory
4919
5035
  });
4920
5036
  logger.info(`[${this.getProviderName()}] Tracked executed tool: ${externalTool.name} with params: ${JSON.stringify(toolInput)}`);
4921
5037
  }
@@ -5023,8 +5139,6 @@ ${errorMsg}
5023
5139
  if (deferredTools.length > 0) {
5024
5140
  logger.info(`[${this.getProviderName()}] Passing ${deferredTools.length} deferred tools for Form generation`);
5025
5141
  }
5026
- logger.info(`[${this.getProviderName()}] passing deferred tools to the matching function: ${JSON.stringify(deferredTools, null, 2)}`);
5027
- logger.info(`[${this.getProviderName()}] passing executed tools to the matching function: ${JSON.stringify(executedToolsList, null, 2)}`);
5028
5142
  const matchResult = await this.matchComponentsFromAnalysis(
5029
5143
  textResponse,
5030
5144
  components,
@@ -9563,7 +9677,8 @@ var SuperatomSDK = class {
9563
9677
  this.geminiApiKey = config.GEMINI_API_KEY || process.env.GEMINI_API_KEY || "";
9564
9678
  this.openaiApiKey = config.OPENAI_API_KEY || process.env.OPENAI_API_KEY || "";
9565
9679
  this.llmProviders = config.LLM_PROVIDERS || getLLMProviders();
9566
- logger.info(`Initializing Superatom SDK v${SDK_VERSION} for project ${this.projectId}, llm providers: ${this.llmProviders.join(", ")}, config llm providers: ${config.LLM_PROVIDERS}`);
9680
+ this.databaseType = config.databaseType || "postgresql";
9681
+ logger.info(`Initializing Superatom SDK v${SDK_VERSION} for project ${this.projectId}, llm providers: ${this.llmProviders.join(", ")}, database type: ${this.databaseType}`);
9567
9682
  this.userManager = new UserManager(this.projectId, 5e3);
9568
9683
  this.dashboardManager = new DashboardManager(this.projectId);
9569
9684
  this.reportManager = new ReportManager(this.projectId);
@@ -9585,8 +9700,9 @@ var SuperatomSDK = class {
9585
9700
  if (promptsDir) {
9586
9701
  promptLoader.setPromptsDir(promptsDir);
9587
9702
  }
9703
+ promptLoader.setDatabaseType(this.databaseType);
9588
9704
  await promptLoader.initialize();
9589
- logger.info(`PromptLoader initialized with ${promptLoader.getCacheSize()} prompts from ${promptLoader.getPromptsDir()}`);
9705
+ logger.info(`PromptLoader initialized with ${promptLoader.getCacheSize()} prompts from ${promptLoader.getPromptsDir()}, database type: ${this.databaseType}`);
9590
9706
  } catch (error) {
9591
9707
  logger.error("Failed to initialize PromptLoader:", error);
9592
9708
  throw error;