@memberjunction/graphql-dataprovider 5.2.0 → 5.3.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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/FieldMapper.ts","../src/graphQLTransactionGroup.ts","../src/graphQLAIClient.ts","../src/storage-providers.ts","../src/graphQLDataProvider.ts","../src/config.ts","../src/rolesAndUsersType.ts","../src/graphQLSystemUserClient.ts","../src/graphQLActionClient.ts","../src/graphQLEncryptionClient.ts","../src/graphQLTestingClient.ts","../src/GraphQLComponentRegistryClient.ts","../src/graphQLVersionHistoryClient.ts","../src/graphQLFileStorageClient.ts"],"sourcesContent":["import { RegisterClass } from '@memberjunction/global';\n\n/**\n * FieldMapper is used to map fields from one name to another. This is useful when we need to map\n * fields from one system to another, or when we need to map fields from one version of a system\n * to another. Uses an internal field mapping but may be overridden or extended as needed.\n */\nexport class FieldMapper {\n private _fieldMap: Record<string, string> = {\n __mj_CreatedAt: '_mj__CreatedAt',\n __mj_UpdatedAt: '_mj__UpdatedAt',\n __mj_DeletedAt: '_mj__DeletedAt',\n };\n\n /**\n * Creates a new FieldMapper instance.\n * @param fieldMap An optional field map to use for mapping fields. If not provided, the default field map will be used.\n */\n constructor() {}\n\n /**\n * Maps fields from one name to another mutating the object in place.\n * @param obj The object to mutate\n */\n public MapFields(obj?: Record<string, unknown>) {\n if (obj) {\n for (const k in obj) {\n if (k in this._fieldMap) {\n obj[this._fieldMap[k]] = obj[k];\n delete obj[k];\n }\n }\n }\n return obj;\n }\n\n /**\n * Maps a field name from one name to another.\n * @param fieldName The field name to map.\n * @returns The mapped field name, or the original field name if no mapping is found.\n */\n public MapFieldName(fieldName: string): string {\n return this._fieldMap[fieldName] ?? fieldName;\n }\n\n /**\n * Maps a field name from one name to another using the reverse mapping.\n * @param fieldName The field name to map.\n * @returns The mapped field name, or the original field name if no mapping is found.\n */\n public ReverseMapFieldName(fieldName: string): string {\n return Object.entries(this._fieldMap).find(([k, v]) => v === fieldName)?.[0] ?? fieldName;\n }\n\n /**\n * Maps fields from one name to another mutating the object in place using the reverse mapping.\n * @param obj The object to mutate\n */\n public ReverseMapFields(obj: Record<string, unknown>) {\n const reversed = Object.fromEntries(Object.entries(this._fieldMap).map(([k, v]) => [v, k]));\n for (const k in obj) {\n if (k in reversed) {\n obj[reversed[k]] = obj[k];\n delete obj[k];\n }\n }\n return obj;\n }\n}\n","import { TransactionGroupBase, TransactionResult } from \"@memberjunction/core\";\nimport { GraphQLDataProvider } from \"./graphQLDataProvider\";\nimport { gql } from \"graphql-request\";\nimport { SafeJSONParse } from \"@memberjunction/global\";\n\nexport class GraphQLTransactionGroup extends TransactionGroupBase {\n private _provider: GraphQLDataProvider;\n constructor(provider: GraphQLDataProvider) {\n super();\n this._provider = provider;\n }\n\n // protected async HandleSubmit(): Promise<TransactionResult[]> {\n // // iterate through each instruction and build up the combined query string\n // // and the combined variables object\n // let combinedQuery = '';\n // let mutationParams = '';\n // const combinedVars: any = {};\n\n // for (let i = 0; i < this.PendingTransactions.length; i++) {\n // const item = this.PendingTransactions[i];\n // let itemMutation = item.Instruction;\n // if (item.Vars) {\n // const keys = Object.keys(item.Vars);\n // // rename the variables to avoid collisions and aggregate the varisables\n // // from the item into our combined variables\n // for (let j = 0; j < keys.length; j++) {\n // const key = keys[j];\n // const newKey = `${key}_${i}`;\n // combinedVars[newKey] = item.Vars[key];\n\n // const keyRegEx = new RegExp('\\\\$' + key, 'g'); // Create the RegExp dynamically with the global flag.\n // itemMutation = itemMutation.replace(keyRegEx, '$' + newKey);\n // const mutationInputType = item.ExtraData.mutationInputTypes.find((t: any) => t.varName === key)?.inputType;\n // //{varName: pk.CodeName, inputType: pk.EntityFieldInfo.GraphQLType + '!'}\n // mutationParams += `$${newKey}: ${mutationInputType} \\n`;\n // }\n // }\n // // add in the specific mutation and give it an alias so we can easily figure out the results\n // // from each of them and pass back properly\n // combinedQuery += `mutation_${i}: ` + itemMutation + '\\n';\n // }\n\n // combinedQuery = `mutation TransactionGroup(${mutationParams}){ \\n` + combinedQuery+ '\\n}'; // wrap it up in a mutation so we can execute it\n // const execResults = await this._provider.ExecuteGQL(combinedQuery, combinedVars)\n // const returnResults: TransactionResult[] = [];\n // for (let i = 0; i < this.PendingTransactions.length; i++) {\n // /// NEED TO TEST TO SEE WHAT ORDER WE GET RESULTS BACK AS\n // const result = execResults[`mutation_${i}`];\n // const item = this.PendingTransactions[i];\n // returnResults.push(new TransactionResult(item, result, result !== null));\n // }\n // return returnResults;\n // }\n\n // new implementation\n protected async HandleSubmit(): Promise<TransactionResult[]> {\n // Define the mutation\n const mutation = gql`\n mutation ExecuteTransactionGroup($group: TransactionInputType!) {\n ExecuteTransactionGroup(group: $group) {\n Success\n ErrorMessages\n ResultsJSON\n }\n }\n `;\n\n // Example variables for the mutation\n const items = [];\n for (const pt of this.PendingTransactions) {\n items.push({\n EntityName: pt.BaseEntity.EntityInfo.Name,\n EntityObjectJSON: await pt.BaseEntity.GetDataObjectJSON(),\n OperationType: pt.OperationType\n });\n }\n const vars = {\n group: {\n Items: items,\n Variables: this.Variables.map(v => {\n return {\n Name: v.Name,\n ItemIndex: this.MapVariableEntityObjectToPosition(v),\n FieldName: v.FieldName,\n Type: v.Type\n }\n }) \n }\n }; \n\n const results = await this._provider.ExecuteGQL(mutation, vars)\n if (results && results.ExecuteTransactionGroup) {\n const data = results.ExecuteTransactionGroup;\n const returnResults: TransactionResult[] = [];\n for (let i = 0; i < this.PendingTransactions.length; i++) {\n const resultJSON = data.ResultsJSON[i];\n const resultObject = SafeJSONParse(resultJSON);\n const item = this.PendingTransactions[i];\n returnResults.push(new TransactionResult(item, resultObject, resultObject !== null));\n }\n return returnResults;\n }\n else {\n throw new Error('Failed to execute transaction group');\n }\n }\n}","import { LogError, LogStatusEx } from \"@memberjunction/core\";\nimport { GraphQLDataProvider } from \"./graphQLDataProvider\";\nimport { gql } from \"graphql-request\";\nimport { ExecuteAgentParams, ExecuteAgentResult } from \"@memberjunction/ai-core-plus\";\nimport { SafeJSONParse } from \"@memberjunction/global\";\n\n/**\n * Client for executing AI operations through GraphQL.\n * This class provides an easy way to execute AI prompts and agents from a client application.\n * \n * The GraphQLAIClient follows the same naming convention as other GraphQL clients\n * in the MemberJunction ecosystem, such as GraphQLActionClient and GraphQLSystemUserClient.\n * \n * @example\n * ```typescript\n * // Create the client\n * const aiClient = new GraphQLAIClient(graphQLProvider);\n * \n * // Run an AI prompt\n * const promptResult = await aiClient.RunAIPrompt({\n * promptId: \"prompt-id\",\n * data: { context: \"user data\" },\n * temperature: 0.7\n * });\n * \n * // Run an AI agent\n * const agentResult = await aiClient.RunAIAgent({\n * agentId: \"agent-id\",\n * messages: [{ role: \"user\", content: \"Hello\" }],\n * sessionId: \"session-123\"\n * });\n * ```\n */\nexport class GraphQLAIClient {\n /**\n * The GraphQLDataProvider instance used to execute GraphQL requests\n * @private\n */\n private _dataProvider: GraphQLDataProvider;\n\n /**\n * Creates a new GraphQLAIClient instance.\n * @param dataProvider The GraphQL data provider to use for queries\n */\n constructor(dataProvider: GraphQLDataProvider) {\n this._dataProvider = dataProvider;\n }\n\n /**\n * Run an AI prompt with the specified parameters.\n * \n * This method invokes an AI prompt on the server through GraphQL and returns the result.\n * Parameters are automatically serialized as needed, and results are parsed for consumption.\n * \n * @param params The parameters for running the AI prompt\n * @returns A Promise that resolves to a RunAIPromptResult object\n * \n * @example\n * ```typescript\n * const result = await aiClient.RunAIPrompt({\n * promptId: \"prompt-id\",\n * data: { key: \"value\" },\n * temperature: 0.7,\n * topP: 0.9\n * });\n * \n * if (result.success) {\n * console.log('Output:', result.output);\n * console.log('Parsed Result:', result.parsedResult);\n * } else {\n * console.error('Error:', result.error);\n * }\n * ```\n */\n public async RunAIPrompt(params: RunAIPromptParams): Promise<RunAIPromptResult> {\n try {\n // Build the mutation with all possible parameters\n const mutation = gql`\n mutation RunAIPrompt(\n $promptId: String!,\n $data: String,\n $overrideModelId: String,\n $overrideVendorId: String,\n $configurationId: String,\n $skipValidation: Boolean,\n $templateData: String,\n $responseFormat: String,\n $temperature: Float,\n $topP: Float,\n $topK: Int,\n $minP: Float,\n $frequencyPenalty: Float,\n $presencePenalty: Float,\n $seed: Int,\n $stopSequences: [String!],\n $includeLogProbs: Boolean,\n $topLogProbs: Int,\n $messages: String,\n $rerunFromPromptRunID: String,\n $systemPromptOverride: String\n ) {\n RunAIPrompt(\n promptId: $promptId,\n data: $data,\n overrideModelId: $overrideModelId,\n overrideVendorId: $overrideVendorId,\n configurationId: $configurationId,\n skipValidation: $skipValidation,\n templateData: $templateData,\n responseFormat: $responseFormat,\n temperature: $temperature,\n topP: $topP,\n topK: $topK,\n minP: $minP,\n frequencyPenalty: $frequencyPenalty,\n presencePenalty: $presencePenalty,\n seed: $seed,\n stopSequences: $stopSequences,\n includeLogProbs: $includeLogProbs,\n topLogProbs: $topLogProbs,\n messages: $messages,\n rerunFromPromptRunID: $rerunFromPromptRunID,\n systemPromptOverride: $systemPromptOverride\n ) {\n success\n output\n parsedResult\n error\n executionTimeMs\n tokensUsed\n promptRunId\n rawResult\n validationResult\n chatResult\n }\n }\n `;\n\n // Prepare variables, serializing complex objects to JSON strings\n const variables = this.preparePromptVariables(params);\n\n // Execute the mutation\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n\n // Process and return the result\n return this.processPromptResult(result);\n } catch (e) {\n return this.handlePromptError(e);\n }\n }\n\n /**\n * Prepares variables for the AI prompt mutation\n * @param params The prompt parameters\n * @returns The prepared variables for GraphQL\n * @private\n */\n private preparePromptVariables(params: RunAIPromptParams): Record<string, any> {\n const variables: Record<string, any> = {\n promptId: params.promptId\n };\n\n // Serialize complex objects to JSON strings\n if (params.data !== undefined) {\n variables.data = typeof params.data === 'object' ? JSON.stringify(params.data) : params.data;\n }\n if (params.templateData !== undefined) {\n variables.templateData = typeof params.templateData === 'object' ? JSON.stringify(params.templateData) : params.templateData;\n }\n if (params.messages !== undefined) {\n variables.messages = JSON.stringify(params.messages);\n }\n\n // Add optional scalar parameters\n if (params.overrideModelId !== undefined) variables.overrideModelId = params.overrideModelId;\n if (params.overrideVendorId !== undefined) variables.overrideVendorId = params.overrideVendorId;\n if (params.configurationId !== undefined) variables.configurationId = params.configurationId;\n if (params.skipValidation !== undefined) variables.skipValidation = params.skipValidation;\n if (params.responseFormat !== undefined) variables.responseFormat = params.responseFormat;\n if (params.temperature !== undefined) variables.temperature = params.temperature;\n if (params.topP !== undefined) variables.topP = params.topP;\n if (params.topK !== undefined) variables.topK = params.topK;\n if (params.minP !== undefined) variables.minP = params.minP;\n if (params.frequencyPenalty !== undefined) variables.frequencyPenalty = params.frequencyPenalty;\n if (params.presencePenalty !== undefined) variables.presencePenalty = params.presencePenalty;\n if (params.seed !== undefined) variables.seed = params.seed;\n if (params.stopSequences !== undefined) variables.stopSequences = params.stopSequences;\n if (params.includeLogProbs !== undefined) variables.includeLogProbs = params.includeLogProbs;\n if (params.topLogProbs !== undefined) variables.topLogProbs = params.topLogProbs;\n if (params.rerunFromPromptRunID !== undefined) variables.rerunFromPromptRunID = params.rerunFromPromptRunID;\n if (params.systemPromptOverride !== undefined) variables.systemPromptOverride = params.systemPromptOverride;\n\n return variables;\n }\n\n /**\n * Processes the result from the AI prompt mutation\n * @param result The raw GraphQL result\n * @returns The processed RunAIPromptResult\n * @private\n */\n private processPromptResult(result: any): RunAIPromptResult {\n if (!result?.RunAIPrompt) {\n throw new Error(\"Invalid response from server\");\n }\n\n const promptResult = result.RunAIPrompt;\n\n // Parse JSON results if they exist\n let parsedResult: any;\n let validationResult: any;\n let chatResult: any;\n\n try {\n if (promptResult.parsedResult) {\n parsedResult = JSON.parse(promptResult.parsedResult);\n }\n } catch (e) {\n // Keep as string if parsing fails\n parsedResult = promptResult.parsedResult;\n }\n\n try {\n if (promptResult.validationResult) {\n validationResult = JSON.parse(promptResult.validationResult);\n }\n } catch (e) {\n validationResult = promptResult.validationResult;\n }\n\n try {\n if (promptResult.chatResult) {\n chatResult = JSON.parse(promptResult.chatResult);\n }\n } catch (e) {\n chatResult = promptResult.chatResult;\n }\n\n return {\n success: promptResult.success,\n output: promptResult.output,\n parsedResult,\n error: promptResult.error,\n executionTimeMs: promptResult.executionTimeMs,\n tokensUsed: promptResult.tokensUsed,\n promptRunId: promptResult.promptRunId,\n rawResult: promptResult.rawResult,\n validationResult,\n chatResult\n };\n }\n\n /**\n * Handles errors in the AI prompt execution\n * @param e The error\n * @returns An error result\n * @private\n */\n private handlePromptError(e: unknown): RunAIPromptResult {\n const error = e as Error;\n LogError(`Error running AI prompt: ${error}`);\n return {\n success: false,\n error: error.message || 'Unknown error occurred'\n };\n }\n\n /**\n * Run an AI agent with the specified parameters.\n *\n * This method invokes an AI agent on the server through GraphQL and returns the result.\n * The agent can maintain conversation context across multiple interactions.\n *\n * If a progress callback is provided in params.onProgress, this method will subscribe\n * to real-time progress updates from the GraphQL server and forward them to the callback.\n *\n * @param params The parameters for running the AI agent\n * @returns A Promise that resolves to a RunAIAgentResult object\n *\n * @example\n * ```typescript\n * const result = await aiClient.RunAIAgent({\n * agentId: \"agent-id\",\n * messages: [\n * { role: \"user\", content: \"What's the weather like?\" }\n * ],\n * sessionId: \"session-123\",\n * data: { location: \"New York\" },\n * onProgress: (progress) => {\n * console.log(`Progress: ${progress.message} (${progress.percentage}%)`);\n * }\n * });\n *\n * if (result.success) {\n * console.log('Response:', result.payload);\n * console.log('Execution time:', result.executionTimeMs, 'ms');\n * } else {\n * console.error('Error:', result.errorMessage);\n * }\n * ```\n */\n public async RunAIAgent(\n params: ExecuteAgentParams,\n sourceArtifactId?: string,\n sourceArtifactVersionId?: string\n ): Promise<ExecuteAgentResult> {\n let subscription: any;\n\n try {\n // Subscribe to progress updates if callback provided\n if (params.onProgress) {\n subscription = this._dataProvider.PushStatusUpdates(this._dataProvider.sessionId)\n .subscribe((message: string) => {\n try {\n LogStatusEx({message: '[GraphQLAIClient] Received statusUpdate message', verboseOnly: true, additionalArgs: [message]});\n const parsed = JSON.parse(message);\n LogStatusEx({message: '[GraphQLAIClient] Parsed message', verboseOnly: true, additionalArgs: [parsed]});\n\n // Filter for ExecutionProgress messages from RunAIAgentResolver\n if (parsed.resolver === 'RunAIAgentResolver' &&\n parsed.type === 'ExecutionProgress' &&\n parsed.status === 'ok' &&\n parsed.data?.progress) {\n\n LogStatusEx({message: '[GraphQLAIClient] Forwarding progress to callback', verboseOnly: true, additionalArgs: [parsed.data.progress]});\n // Forward progress to callback with agentRunId in metadata\n const progressWithRunId = {\n ...parsed.data.progress,\n metadata: {\n ...(parsed.data.progress.metadata || {}),\n agentRunId: parsed.data.agentRunId\n }\n };\n params.onProgress!(progressWithRunId);\n } else {\n LogStatusEx({message: '[GraphQLAIClient] Message does not match filter criteria', verboseOnly: true, additionalArgs: [{\n resolver: parsed.resolver,\n type: parsed.type,\n status: parsed.status,\n hasProgress: !!parsed.data?.progress\n }]});\n }\n } catch (e) {\n // Log parsing errors for debugging\n console.error('[GraphQLAIClient] Failed to parse progress message:', e, 'Raw message:', message);\n }\n });\n }\n\n // Build the mutation\n const mutation = gql`\n mutation RunAIAgent(\n $agentId: String!,\n $messages: String!,\n $sessionId: String!,\n $data: String,\n $payload: String,\n $templateData: String,\n $lastRunId: String,\n $autoPopulateLastRunPayload: Boolean,\n $configurationId: String,\n $conversationDetailId: String,\n $createArtifacts: Boolean,\n $createNotification: Boolean,\n $sourceArtifactId: String,\n $sourceArtifactVersionId: String\n ) {\n RunAIAgent(\n agentId: $agentId,\n messages: $messages,\n sessionId: $sessionId,\n data: $data,\n payload: $payload,\n templateData: $templateData,\n lastRunId: $lastRunId,\n autoPopulateLastRunPayload: $autoPopulateLastRunPayload,\n configurationId: $configurationId,\n conversationDetailId: $conversationDetailId,\n createArtifacts: $createArtifacts,\n createNotification: $createNotification,\n sourceArtifactId: $sourceArtifactId,\n sourceArtifactVersionId: $sourceArtifactVersionId\n ) {\n success\n errorMessage\n executionTimeMs\n result\n }\n }\n `;\n\n // Prepare variables\n const variables = this.prepareAgentVariables(params, sourceArtifactId, sourceArtifactVersionId);\n\n // Execute the mutation\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n\n // Process and return the result\n return this.processAgentResult(result.RunAIAgent?.result);\n } catch (e) {\n return this.handleAgentError(e);\n } finally {\n // Always clean up subscription\n if (subscription) {\n subscription.unsubscribe();\n }\n }\n }\n\n /**\n * Prepares variables for the AI agent mutation\n * @param params The agent parameters\n * @param sourceArtifactId Optional source artifact ID for versioning\n * @param sourceArtifactVersionId Optional source artifact version ID for versioning\n * @returns The prepared variables for GraphQL\n * @private\n */\n private prepareAgentVariables(\n params: ExecuteAgentParams,\n sourceArtifactId?: string,\n sourceArtifactVersionId?: string\n ): Record<string, any> {\n const variables: Record<string, any> = {\n agentId: params.agent.ID,\n messages: JSON.stringify(params.conversationMessages),\n sessionId: this._dataProvider.sessionId\n };\n\n // Serialize optional complex objects to JSON strings\n if (params.data !== undefined) {\n variables.data = typeof params.data === 'object' ? JSON.stringify(params.data) : params.data;\n } \n if (params.payload !== undefined) {\n variables.payload = typeof params.payload === 'object' ? JSON.stringify(params.payload) : params.payload;\n }\n\n // Add optional scalar parameters\n if (params.lastRunId !== undefined) variables.lastRunId = params.lastRunId;\n if (params.autoPopulateLastRunPayload !== undefined) variables.autoPopulateLastRunPayload = params.autoPopulateLastRunPayload;\n if (params.configurationId !== undefined) variables.configurationId = params.configurationId;\n if (params.conversationDetailId !== undefined) {\n variables.conversationDetailId = params.conversationDetailId;\n // When conversationDetailId is provided, enable server-side artifact and notification creation\n // This is a GraphQL resolver-level concern, not agent execution concern\n variables.createArtifacts = true;\n variables.createNotification = true;\n }\n // Add source artifact tracking for versioning (GraphQL resolver-level concern)\n if (sourceArtifactId !== undefined) variables.sourceArtifactId = sourceArtifactId;\n if (sourceArtifactVersionId !== undefined) variables.sourceArtifactVersionId = sourceArtifactVersionId;\n\n return variables;\n }\n\n /**\n * Processes the result from the AI agent mutation\n * @param result The raw GraphQL result\n * @returns The processed RunAIAgentResult\n * @private\n */\n private processAgentResult(result: string): ExecuteAgentResult {\n return SafeJSONParse(result) as ExecuteAgentResult; \n }\n\n /**\n * Handles errors in the AI agent execution\n * @param e The error\n * @returns An error result\n * @private\n */\n private handleAgentError(e: unknown): ExecuteAgentResult {\n const error = e as Error;\n LogError(`Error running AI agent: ${error}`);\n return {\n success: false,\n agentRun: undefined\n };\n }\n\n /**\n * Run an AI agent using an existing conversation detail ID.\n * This is an optimized method that loads conversation history server-side,\n * avoiding the need to send large attachment data from client to server.\n *\n * Use this method when:\n * - The user's message has already been saved as a ConversationDetail\n * - The conversation may have attachments (images, documents, etc.)\n * - You want optimal performance by loading history on the server\n *\n * @param params The parameters for running the AI agent from conversation detail\n * @returns A Promise that resolves to an ExecuteAgentResult object\n *\n * @example\n * ```typescript\n * const result = await aiClient.RunAIAgentFromConversationDetail({\n * conversationDetailId: \"detail-id-123\",\n * agentId: \"agent-id\",\n * maxHistoryMessages: 20,\n * createArtifacts: true,\n * onProgress: (progress) => {\n * console.log(`Progress: ${progress.message}`);\n * }\n * });\n * ```\n */\n public async RunAIAgentFromConversationDetail(\n params: RunAIAgentFromConversationDetailParams\n ): Promise<ExecuteAgentResult> {\n let subscription: ReturnType<typeof this._dataProvider.PushStatusUpdates.prototype.subscribe> | undefined;\n\n try {\n // Subscribe to progress updates if callback provided\n if (params.onProgress) {\n subscription = this._dataProvider.PushStatusUpdates(this._dataProvider.sessionId)\n .subscribe((message: string) => {\n try {\n const parsed = JSON.parse(message);\n\n // Filter for ExecutionProgress messages from RunAIAgentResolver\n if (parsed.resolver === 'RunAIAgentResolver' &&\n parsed.type === 'ExecutionProgress' &&\n parsed.status === 'ok' &&\n parsed.data?.progress) {\n\n // Forward progress to callback with agentRunId in metadata\n const progressWithRunId = {\n ...parsed.data.progress,\n metadata: {\n ...(parsed.data.progress.metadata || {}),\n agentRunId: parsed.data.agentRunId\n }\n };\n params.onProgress!(progressWithRunId);\n }\n } catch (e) {\n console.error('[GraphQLAIClient] Failed to parse progress message:', e);\n }\n });\n }\n\n // Build the mutation\n const mutation = gql`\n mutation RunAIAgentFromConversationDetail(\n $conversationDetailId: String!,\n $agentId: String!,\n $sessionId: String!,\n $maxHistoryMessages: Int,\n $data: String,\n $payload: String,\n $lastRunId: String,\n $autoPopulateLastRunPayload: Boolean,\n $configurationId: String,\n $createArtifacts: Boolean,\n $createNotification: Boolean,\n $sourceArtifactId: String,\n $sourceArtifactVersionId: String\n ) {\n RunAIAgentFromConversationDetail(\n conversationDetailId: $conversationDetailId,\n agentId: $agentId,\n sessionId: $sessionId,\n maxHistoryMessages: $maxHistoryMessages,\n data: $data,\n payload: $payload,\n lastRunId: $lastRunId,\n autoPopulateLastRunPayload: $autoPopulateLastRunPayload,\n configurationId: $configurationId,\n createArtifacts: $createArtifacts,\n createNotification: $createNotification,\n sourceArtifactId: $sourceArtifactId,\n sourceArtifactVersionId: $sourceArtifactVersionId\n ) {\n success\n errorMessage\n executionTimeMs\n result\n }\n }\n `;\n\n // Prepare variables\n const variables: Record<string, unknown> = {\n conversationDetailId: params.conversationDetailId,\n agentId: params.agentId,\n sessionId: this._dataProvider.sessionId\n };\n\n // Add optional parameters\n if (params.maxHistoryMessages !== undefined) variables.maxHistoryMessages = params.maxHistoryMessages;\n if (params.data !== undefined) {\n variables.data = typeof params.data === 'object' ? JSON.stringify(params.data) : params.data;\n }\n if (params.payload !== undefined) {\n variables.payload = typeof params.payload === 'object' ? JSON.stringify(params.payload) : params.payload;\n }\n if (params.lastRunId !== undefined) variables.lastRunId = params.lastRunId;\n if (params.autoPopulateLastRunPayload !== undefined) variables.autoPopulateLastRunPayload = params.autoPopulateLastRunPayload;\n if (params.configurationId !== undefined) variables.configurationId = params.configurationId;\n if (params.createArtifacts !== undefined) variables.createArtifacts = params.createArtifacts;\n if (params.createNotification !== undefined) variables.createNotification = params.createNotification;\n if (params.sourceArtifactId !== undefined) variables.sourceArtifactId = params.sourceArtifactId;\n if (params.sourceArtifactVersionId !== undefined) variables.sourceArtifactVersionId = params.sourceArtifactVersionId;\n\n // Execute the mutation\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n\n // Process and return the result\n return this.processAgentResult(result.RunAIAgentFromConversationDetail?.result);\n } catch (e) {\n return this.handleAgentError(e);\n } finally {\n // Always clean up subscription\n if (subscription) {\n subscription.unsubscribe();\n }\n }\n }\n\n /**\n * Execute a simple prompt without requiring a stored AI Prompt entity.\n * This method is designed for interactive components that need quick AI responses.\n * \n * @param params The parameters for the simple prompt execution\n * @returns A Promise that resolves to a SimplePromptResult object\n * \n * @example\n * ```typescript\n * const result = await aiClient.ExecuteSimplePrompt({\n * systemPrompt: \"You are a helpful assistant\",\n * messages: [\n * { message: \"What's the weather?\", role: \"user\" }\n * ],\n * modelPower: \"medium\"\n * });\n * \n * if (result.success) {\n * console.log('Response:', result.result);\n * if (result.resultObject) {\n * console.log('Parsed JSON:', JSON.parse(result.resultObject));\n * }\n * }\n * ```\n */\n public async ExecuteSimplePrompt(params: ExecuteSimplePromptParams): Promise<SimplePromptResult> {\n try {\n const mutation = gql`\n mutation ExecuteSimplePrompt(\n $systemPrompt: String!,\n $messages: String,\n $preferredModels: [String!],\n $modelPower: String,\n $responseFormat: String\n ) {\n ExecuteSimplePrompt(\n systemPrompt: $systemPrompt,\n messages: $messages,\n preferredModels: $preferredModels,\n modelPower: $modelPower,\n responseFormat: $responseFormat\n ) {\n success\n result\n resultObject\n modelName\n error\n executionTimeMs\n }\n }\n `;\n\n // Prepare variables\n const variables: Record<string, any> = {\n systemPrompt: params.systemPrompt\n };\n\n // Convert messages array to JSON string if provided\n if (params.messages && params.messages.length > 0) {\n variables.messages = JSON.stringify(params.messages);\n }\n\n if (params.preferredModels) {\n variables.preferredModels = params.preferredModels;\n }\n\n if (params.modelPower) {\n variables.modelPower = params.modelPower;\n }\n\n if (params.responseFormat) {\n variables.responseFormat = params.responseFormat;\n }\n\n // Execute the mutation\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n\n if (!result?.ExecuteSimplePrompt) {\n throw new Error(\"Invalid response from server\");\n }\n\n const promptResult = result.ExecuteSimplePrompt;\n\n // Parse resultObject if it exists\n let resultObject: any;\n if (promptResult.resultObject) {\n try {\n resultObject = JSON.parse(promptResult.resultObject);\n } catch (e) {\n resultObject = promptResult.resultObject;\n }\n }\n\n return {\n success: promptResult.success,\n result: promptResult.result,\n resultObject,\n modelName: promptResult.modelName,\n error: promptResult.error,\n executionTimeMs: promptResult.executionTimeMs\n };\n\n } catch (e) {\n const error = e as Error;\n LogError(`Error executing simple prompt: ${error}`);\n return {\n success: false,\n modelName: 'Unknown',\n error: error.message || 'Unknown error occurred'\n };\n }\n }\n\n /**\n * Generate embeddings for text using local embedding models.\n * This method is designed for interactive components that need fast similarity calculations.\n * \n * @param params The parameters for embedding generation\n * @returns A Promise that resolves to an EmbedTextResult object\n * \n * @example\n * ```typescript\n * const result = await aiClient.EmbedText({\n * textToEmbed: [\"Hello world\", \"How are you?\"],\n * modelSize: \"small\"\n * });\n * \n * console.log('Embeddings:', result.embeddings);\n * console.log('Model used:', result.modelName);\n * console.log('Dimensions:', result.vectorDimensions);\n * ```\n */\n public async EmbedText(params: EmbedTextParams): Promise<EmbedTextResult> {\n try {\n const mutation = gql`\n mutation EmbedText(\n $textToEmbed: [String!]!,\n $modelSize: String!\n ) {\n EmbedText(\n textToEmbed: $textToEmbed,\n modelSize: $modelSize\n ) {\n embeddings\n modelName\n vectorDimensions\n error\n }\n }\n `;\n\n // Prepare variables - handle both single string and array\n const textArray = Array.isArray(params.textToEmbed) \n ? params.textToEmbed \n : [params.textToEmbed];\n\n const variables = {\n textToEmbed: textArray,\n modelSize: params.modelSize\n };\n\n // Execute the mutation\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n\n if (!result?.EmbedText) {\n throw new Error(\"Invalid response from server\");\n }\n\n const embedResult = result.EmbedText;\n\n // Return single embedding or array based on input\n const returnEmbeddings = Array.isArray(params.textToEmbed)\n ? embedResult.embeddings\n : embedResult.embeddings[0];\n\n return {\n embeddings: returnEmbeddings,\n modelName: embedResult.modelName,\n vectorDimensions: embedResult.vectorDimensions,\n error: embedResult.error\n };\n\n } catch (e) {\n const error = e as Error;\n LogError(`Error generating embeddings: ${error}`);\n return {\n embeddings: Array.isArray(params.textToEmbed) ? [] : [],\n modelName: 'Unknown',\n vectorDimensions: 0,\n error: error.message || 'Unknown error occurred'\n };\n }\n }\n}\n\n/**\n * Parameters for executing a simple prompt\n */\nexport interface ExecuteSimplePromptParams {\n /**\n * The system prompt to set context for the model\n */\n systemPrompt: string;\n \n /**\n * Optional message history\n */\n messages?: Array<{message: string, role: 'user' | 'assistant'}>;\n \n /**\n * Preferred model names to use\n */\n preferredModels?: string[];\n \n /**\n * Power level for model selection\n */\n modelPower?: 'lowest' | 'medium' | 'highest';\n \n /**\n * Response format (e.g., \"json\")\n */\n responseFormat?: string;\n}\n\n/**\n * Result from executing a simple prompt\n */\nexport interface SimplePromptResult {\n /**\n * Whether the execution was successful\n */\n success: boolean;\n \n /**\n * The text response from the model\n */\n result?: string;\n \n /**\n * Parsed JSON object if the response contained JSON\n */\n resultObject?: any;\n \n /**\n * Name of the model used\n */\n modelName: string;\n \n /**\n * Error message if execution failed\n */\n error?: string;\n \n /**\n * Execution time in milliseconds\n */\n executionTimeMs?: number;\n}\n\n/**\n * Parameters for generating text embeddings\n */\nexport interface EmbedTextParams {\n /**\n * Text or array of texts to embed\n */\n textToEmbed: string | string[];\n \n /**\n * Size of the embedding model to use\n */\n modelSize: 'small' | 'medium';\n}\n\n/**\n * Result from generating text embeddings\n */\nexport interface EmbedTextResult {\n /**\n * Single embedding vector or array of vectors\n */\n embeddings: number[] | number[][];\n \n /**\n * Name of the model used\n */\n modelName: string;\n \n /**\n * Number of dimensions in each vector\n */\n vectorDimensions: number;\n \n /**\n * Error message if generation failed\n */\n error?: string;\n}\n\n/**\n * Parameters for running an AI prompt\n */\nexport interface RunAIPromptParams {\n /**\n * The ID of the AI prompt to run\n */\n promptId: string;\n \n /**\n * Data context to pass to the prompt (will be JSON serialized)\n */\n data?: Record<string, any>;\n \n /**\n * Override the default model ID\n */\n overrideModelId?: string;\n \n /**\n * Override the default vendor ID\n */\n overrideVendorId?: string;\n \n /**\n * Configuration ID to use\n */\n configurationId?: string;\n \n /**\n * Skip validation of the prompt\n */\n skipValidation?: boolean;\n \n /**\n * Template data for prompt templating (will be JSON serialized)\n */\n templateData?: Record<string, any>;\n \n /**\n * Response format (e.g., \"json\", \"text\")\n */\n responseFormat?: string;\n \n /**\n * Temperature parameter for the model (0.0 to 1.0)\n */\n temperature?: number;\n \n /**\n * Top-p sampling parameter\n */\n topP?: number;\n \n /**\n * Top-k sampling parameter\n */\n topK?: number;\n \n /**\n * Min-p sampling parameter\n */\n minP?: number;\n \n /**\n * Frequency penalty parameter\n */\n frequencyPenalty?: number;\n \n /**\n * Presence penalty parameter\n */\n presencePenalty?: number;\n \n /**\n * Random seed for reproducible outputs\n */\n seed?: number;\n \n /**\n * Stop sequences for the model\n */\n stopSequences?: string[];\n \n /**\n * Include log probabilities in the response\n */\n includeLogProbs?: boolean;\n \n /**\n * Number of top log probabilities to include\n */\n topLogProbs?: number;\n \n /**\n * Conversation messages (will be JSON serialized)\n */\n messages?: Array<{ role: string; content: string }>;\n \n /**\n * ID of a previous prompt run to rerun from\n */\n rerunFromPromptRunID?: string;\n \n /**\n * Override the system prompt\n */\n systemPromptOverride?: string;\n}\n\n/**\n * Result from running an AI prompt\n */\nexport interface RunAIPromptResult {\n /**\n * Whether the prompt execution was successful\n */\n success: boolean;\n \n /**\n * The output from the prompt\n */\n output?: string;\n \n /**\n * Parsed result data (if applicable)\n */\n parsedResult?: any;\n \n /**\n * Error message if the execution failed\n */\n error?: string;\n \n /**\n * Execution time in milliseconds\n */\n executionTimeMs?: number;\n \n /**\n * Number of tokens used\n */\n tokensUsed?: number;\n \n /**\n * ID of the prompt run record\n */\n promptRunId?: string;\n \n /**\n * Raw result from the model\n */\n rawResult?: string;\n \n /**\n * Validation result data\n */\n validationResult?: any;\n \n /**\n * Chat completion result data\n */\n chatResult?: any;\n}\n\n/**\n * Parameters for running an AI agent from an existing conversation detail.\n * This is the optimized method that loads conversation history server-side.\n */\nexport interface RunAIAgentFromConversationDetailParams {\n /**\n * The ID of the conversation detail (user's message) to use as context\n */\n conversationDetailId: string;\n\n /**\n * The ID of the AI agent to run\n */\n agentId: string;\n\n /**\n * Maximum number of history messages to include (default: 20)\n */\n maxHistoryMessages?: number;\n\n /**\n * Data context to pass to the agent (will be JSON serialized)\n */\n data?: Record<string, unknown>;\n\n /**\n * Payload to pass to the agent (will be JSON serialized)\n */\n payload?: Record<string, unknown> | string;\n\n /**\n * ID of the last agent run for continuity\n */\n lastRunId?: string;\n\n /**\n * Whether to auto-populate payload from last run\n */\n autoPopulateLastRunPayload?: boolean;\n\n /**\n * Configuration ID to use\n */\n configurationId?: string;\n\n /**\n * Whether to create artifacts from the agent's payload\n */\n createArtifacts?: boolean;\n\n /**\n * Whether to create a user notification on completion\n */\n createNotification?: boolean;\n\n /**\n * Source artifact ID for versioning\n */\n sourceArtifactId?: string;\n\n /**\n * Source artifact version ID for versioning\n */\n sourceArtifactVersionId?: string;\n\n /**\n * Optional callback for progress updates\n */\n onProgress?: (progress: {\n currentStep: string;\n percentage?: number;\n message: string;\n metadata?: Record<string, unknown>;\n }) => void;\n}\n","import { ILocalStorageProvider, LogError, LogErrorEx } from '@memberjunction/core';\nimport { openDB, DBSchema, IDBPDatabase } from '@tempfix/idb';\n\n// Default category used when no category is specified\nconst DEFAULT_CATEGORY = 'default';\n\n// ============================================================================\n// IN-MEMORY STORAGE PROVIDER (Map of Maps)\n// ============================================================================\n\n/**\n * In-memory storage provider using nested Map structure for category isolation.\n * Used as a fallback when browser storage is not available.\n *\n * Storage structure: Map<category, Map<key, value>>\n */\nexport class BrowserStorageProviderBase implements ILocalStorageProvider {\n private _storage: Map<string, Map<string, string>> = new Map();\n\n /**\n * Gets or creates a category map\n */\n private getCategoryMap(category: string): Map<string, string> {\n const cat = category || DEFAULT_CATEGORY;\n let categoryMap = this._storage.get(cat);\n if (!categoryMap) {\n categoryMap = new Map();\n this._storage.set(cat, categoryMap);\n }\n return categoryMap;\n }\n\n public async GetItem(key: string, category?: string): Promise<string | null> {\n const categoryMap = this.getCategoryMap(category || DEFAULT_CATEGORY);\n return categoryMap.get(key) ?? null;\n }\n\n public async SetItem(key: string, value: string, category?: string): Promise<void> {\n const categoryMap = this.getCategoryMap(category || DEFAULT_CATEGORY);\n categoryMap.set(key, value);\n }\n\n public async Remove(key: string, category?: string): Promise<void> {\n const categoryMap = this.getCategoryMap(category || DEFAULT_CATEGORY);\n categoryMap.delete(key);\n }\n\n public async ClearCategory(category: string): Promise<void> {\n const cat = category || DEFAULT_CATEGORY;\n this._storage.delete(cat);\n }\n\n public async GetCategoryKeys(category: string): Promise<string[]> {\n const categoryMap = this._storage.get(category || DEFAULT_CATEGORY);\n return categoryMap ? Array.from(categoryMap.keys()) : [];\n }\n}\n\n// ============================================================================\n// BROWSER LOCAL STORAGE PROVIDER (Key Prefix)\n// ============================================================================\n\n/**\n * Browser localStorage provider with category support via key prefixing.\n *\n * Key format: [mj]:[category]:[key]\n * Example: [mj]:[RunViewCache]:[Users|Active=1|Name ASC]\n *\n * Falls back to in-memory storage if localStorage is not available.\n */\nclass BrowserLocalStorageProvider extends BrowserStorageProviderBase {\n /**\n * Builds a prefixed key for localStorage\n * Format: [mj]:[category]:[key]\n */\n private buildKey(key: string, category?: string): string {\n const cat = category || DEFAULT_CATEGORY;\n return `[mj]:[${cat}]:[${key}]`;\n }\n\n /**\n * Parses a prefixed key to extract category and original key\n */\n private parseKey(prefixedKey: string): { category: string; key: string } | null {\n const match = prefixedKey.match(/^\\[mj\\]:\\[([^\\]]*)\\]:\\[(.+)\\]$/);\n if (match) {\n return { category: match[1], key: match[2] };\n }\n return null;\n }\n\n public override async GetItem(key: string, category?: string): Promise<string | null> {\n if (typeof localStorage !== 'undefined') {\n return localStorage.getItem(this.buildKey(key, category));\n }\n return await super.GetItem(key, category);\n }\n\n public override async SetItem(key: string, value: string, category?: string): Promise<void> {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(this.buildKey(key, category), value);\n } else {\n await super.SetItem(key, value, category);\n }\n }\n\n public override async Remove(key: string, category?: string): Promise<void> {\n if (typeof localStorage !== 'undefined') {\n localStorage.removeItem(this.buildKey(key, category));\n } else {\n await super.Remove(key, category);\n }\n }\n\n public override async ClearCategory(category: string): Promise<void> {\n if (typeof localStorage !== 'undefined') {\n const cat = category || DEFAULT_CATEGORY;\n const prefix = `[mj]:[${cat}]:`;\n const keysToRemove: string[] = [];\n\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key && key.startsWith(prefix)) {\n keysToRemove.push(key);\n }\n }\n\n for (const key of keysToRemove) {\n localStorage.removeItem(key);\n }\n } else {\n await super.ClearCategory(category);\n }\n }\n\n public override async GetCategoryKeys(category: string): Promise<string[]> {\n if (typeof localStorage !== 'undefined') {\n const cat = category || DEFAULT_CATEGORY;\n const prefix = `[mj]:[${cat}]:`;\n const keys: string[] = [];\n\n for (let i = 0; i < localStorage.length; i++) {\n const prefixedKey = localStorage.key(i);\n if (prefixedKey && prefixedKey.startsWith(prefix)) {\n const parsed = this.parseKey(prefixedKey);\n if (parsed) {\n keys.push(parsed.key);\n }\n }\n }\n\n return keys;\n }\n return await super.GetCategoryKeys(category);\n }\n}\n\n// ============================================================================\n// INDEXED DB STORAGE PROVIDER (Object Stores per Category)\n// ============================================================================\n\nconst IDB_DB_NAME = 'MJ_Metadata';\nconst IDB_DB_VERSION = 3; // v3: Remove legacy Metadata_KVPairs store\n\n// Known object store names as a const tuple for type safety\nconst KNOWN_OBJECT_STORES = [\n 'mj:default', // Default category\n 'mj:Metadata', // Metadata cache\n 'mj:RunViewCache', // RunView results cache\n 'mj:RunQueryCache', // RunQuery results cache\n 'mj:DatasetCache', // Dataset cache\n] as const;\n\n// Type for known store names\ntype KnownStoreName = typeof KNOWN_OBJECT_STORES[number];\n\n// Legacy store name - kept for cleanup during upgrade\nconst LEGACY_STORE_NAME = 'Metadata_KVPairs';\n\n/**\n * IndexedDB schema with dynamic object stores per category.\n * Each category gets its own object store: mj:CategoryName\n */\nexport interface MJ_MetadataDB extends DBSchema {\n // Default category store\n 'mj:default': {\n key: string;\n value: string;\n };\n // Metadata store\n 'mj:Metadata': {\n key: string;\n value: string;\n };\n // RunView cache store\n 'mj:RunViewCache': {\n key: string;\n value: string;\n };\n // RunQuery cache store\n 'mj:RunQueryCache': {\n key: string;\n value: string;\n };\n // Dataset cache store\n 'mj:DatasetCache': {\n key: string;\n value: string;\n };\n}\n\n/**\n * IndexedDB storage provider with category support via separate object stores.\n *\n * Known categories (mj:Metadata, mj:RunViewCache, etc.) get dedicated object stores.\n * Unknown categories fall back to the default store with prefixed keys.\n */\nexport class BrowserIndexedDBStorageProvider extends BrowserStorageProviderBase {\n private dbPromise: Promise<IDBPDatabase<MJ_MetadataDB>>;\n private _dbReady: boolean = false;\n\n constructor() {\n super();\n this.dbPromise = openDB<MJ_MetadataDB>(IDB_DB_NAME, IDB_DB_VERSION, {\n upgrade(db) {\n try {\n // Remove legacy store if it exists (cleanup from v1/v2)\n // Cast needed because LEGACY_STORE_NAME is not in current schema (it's being removed)\n if (db.objectStoreNames.contains(LEGACY_STORE_NAME as KnownStoreName)) {\n db.deleteObjectStore(LEGACY_STORE_NAME as KnownStoreName);\n }\n\n // Create known category stores\n for (const storeName of KNOWN_OBJECT_STORES) {\n if (!db.objectStoreNames.contains(storeName)) {\n db.createObjectStore(storeName);\n }\n }\n } catch (e) {\n LogErrorEx({\n error: e,\n message: (e as Error)?.message\n });\n }\n },\n });\n\n this.dbPromise.then(() => {\n this._dbReady = true;\n }).catch(e => {\n LogErrorEx({\n error: e,\n message: 'IndexedDB initialization failed: ' + (e as Error)?.message\n });\n });\n }\n\n /**\n * Checks if a category has a dedicated object store\n */\n private isKnownCategory(category: string): boolean {\n const storeName = `mj:${category}`;\n return (KNOWN_OBJECT_STORES as readonly string[]).includes(storeName);\n }\n\n /**\n * Gets the object store name for a category.\n * Returns the dedicated store if it exists, otherwise returns the default store.\n */\n private getStoreName(category?: string): KnownStoreName {\n const cat = category || DEFAULT_CATEGORY;\n if (this.isKnownCategory(cat)) {\n return `mj:${cat}` as KnownStoreName;\n }\n return 'mj:default';\n }\n\n /**\n * Gets the key to use in the store.\n * For known stores, use the key as-is.\n * For unknown categories using the default store, prefix with category.\n */\n private getStoreKey(key: string, category?: string): string {\n const cat = category || DEFAULT_CATEGORY;\n if (this.isKnownCategory(cat)) {\n return key;\n }\n // If using default store for unknown category, prefix the key\n return `[${cat}]:${key}`;\n }\n\n public override async SetItem(key: string, value: string, category?: string): Promise<void> {\n try {\n const db = await this.dbPromise;\n const storeName = this.getStoreName(category);\n const storeKey = this.getStoreKey(key, category);\n\n const tx = db.transaction(storeName, 'readwrite');\n await tx.objectStore(storeName).put(value, storeKey);\n await tx.done;\n } catch (e) {\n LogErrorEx({\n error: e,\n message: (e as Error)?.message\n });\n // Fall back to in-memory\n await super.SetItem(key, value, category);\n }\n }\n\n public override async GetItem(key: string, category?: string): Promise<string | null> {\n try {\n const db = await this.dbPromise;\n const storeName = this.getStoreName(category);\n const storeKey = this.getStoreKey(key, category);\n\n const value = await db.transaction(storeName).objectStore(storeName).get(storeKey);\n return value ?? null;\n } catch (e) {\n LogErrorEx({\n error: e,\n message: (e as Error)?.message\n });\n // Fall back to in-memory\n return await super.GetItem(key, category);\n }\n }\n\n public override async Remove(key: string, category?: string): Promise<void> {\n try {\n const db = await this.dbPromise;\n const storeName = this.getStoreName(category);\n const storeKey = this.getStoreKey(key, category);\n\n const tx = db.transaction(storeName, 'readwrite');\n await tx.objectStore(storeName).delete(storeKey);\n await tx.done;\n } catch (e) {\n LogErrorEx({\n error: e,\n message: (e as Error)?.message\n });\n // Fall back to in-memory\n await super.Remove(key, category);\n }\n }\n\n public override async ClearCategory(category: string): Promise<void> {\n try {\n const db = await this.dbPromise;\n const cat = category || DEFAULT_CATEGORY;\n const storeName = this.getStoreName(category);\n\n // If it's a dedicated store, clear the entire store\n if (this.isKnownCategory(cat)) {\n const tx = db.transaction(storeName, 'readwrite');\n await tx.objectStore(storeName).clear();\n await tx.done;\n } else {\n // For unknown categories using default store, clear only prefixed keys\n const prefix = `[${cat}]:`;\n const tx = db.transaction('mj:default', 'readwrite');\n const store = tx.objectStore('mj:default');\n const allKeys = await store.getAllKeys();\n\n for (const storeKey of allKeys) {\n if (typeof storeKey === 'string' && storeKey.startsWith(prefix)) {\n await store.delete(storeKey);\n }\n }\n await tx.done;\n }\n } catch (e) {\n LogErrorEx({\n error: e,\n message: (e as Error)?.message\n });\n // Fall back to in-memory\n await super.ClearCategory(category);\n }\n }\n\n public override async GetCategoryKeys(category: string): Promise<string[]> {\n try {\n const db = await this.dbPromise;\n const cat = category || DEFAULT_CATEGORY;\n const storeName = this.getStoreName(category);\n\n const tx = db.transaction(storeName, 'readonly');\n const store = tx.objectStore(storeName);\n const allKeys = await store.getAllKeys();\n\n // If it's a dedicated store, return keys as-is\n if (this.isKnownCategory(cat)) {\n return allKeys.map(k => String(k));\n }\n\n // For unknown categories, filter and strip prefix\n const prefix = `[${cat}]:`;\n return allKeys\n .map(k => String(k))\n .filter(k => k.startsWith(prefix))\n .map(k => k.slice(prefix.length));\n } catch (e) {\n LogErrorEx({\n error: e,\n message: (e as Error)?.message\n });\n // Fall back to in-memory\n return await super.GetCategoryKeys(category);\n }\n }\n}\n","/**************************************************************************************************************\n * The graphQLDataProvider provides a data provider for the entities framework that uses GraphQL to communicate\n * with the server.\n * In practice - this FILE will NOT exist in the entities library, we need to move to its own separate project\n * so it is only included by the consumer of the entities library if they want to use it.\n**************************************************************************************************************/\n\nimport { BaseEntity, IEntityDataProvider, IMetadataProvider, IRunViewProvider, ProviderConfigDataBase, RunViewResult,\n EntityInfo, EntityFieldInfo, EntityFieldTSType,\n RunViewParams, ProviderBase, ProviderType, UserInfo, UserRoleInfo, RecordChange,\n ILocalStorageProvider, EntitySaveOptions, EntityMergeOptions, LogError,\n TransactionGroupBase, TransactionItem, DatasetItemFilterType, DatasetResultType, DatasetStatusResultType, EntityRecordNameInput,\n EntityRecordNameResult, IRunReportProvider, RunReportResult, RunReportParams, RecordDependency, RecordMergeRequest, RecordMergeResult,\n RunQueryResult, PotentialDuplicateRequest, PotentialDuplicateResponse, CompositeKey, EntityDeleteOptions,\n RunQueryParams, BaseEntityResult,\n RunViewWithCacheCheckParams, RunViewsWithCacheCheckResponse, RunViewWithCacheCheckResult,\n RunQueryWithCacheCheckParams, RunQueriesWithCacheCheckResponse, RunQueryWithCacheCheckResult,\n KeyValuePair, getGraphQLTypeNameBase, AggregateExpression, InMemoryLocalStorageProvider } from \"@memberjunction/core\";\nimport { MJUserViewEntityExtended, ViewInfo } from '@memberjunction/core-entities'\n\nimport { gql, GraphQLClient } from 'graphql-request'\nimport { Observable, Subject, Subscription } from 'rxjs';\nimport { Client, createClient } from 'graphql-ws';\nimport { FieldMapper } from './FieldMapper';\nimport { v4 as uuidv4 } from 'uuid';\nimport { GraphQLTransactionGroup } from \"./graphQLTransactionGroup\";\nimport { GraphQLAIClient } from \"./graphQLAIClient\";\nimport { BrowserIndexedDBStorageProvider } from \"./storage-providers\";\n\n// define the shape for a RefreshToken function that can be called by the GraphQLDataProvider whenever it receives an exception that the JWT it has already is expired\nexport type RefreshTokenFunction = () => Promise<string>;\n\n/**\n * The GraphQLProviderConfigData class is used to configure the GraphQLDataProvider. It is passed to the Config method of the GraphQLDataProvider\n */\nexport class GraphQLProviderConfigData extends ProviderConfigDataBase {\n /**\n * Token is the JWT token that is used to authenticate the user with the server\n */\n get Token(): string { return this.Data.Token }\n\n set Token(token: string) { this.Data.Token = token}\n\n /**\n * This optional parameter is used when using a shared secret key that is static and provided by the publisher of the MJAPI server. Providing this value will result in\n * a special header x-mj-api-key being set with this value in the HTTP request to the server. This is useful when the server is configured to require this key for certain requests.\n *\n * WARNING: This should NEVER BE USED IN A CLIENT APP like a browser. The only suitable use for this is if you are using GraphQLDataProvider on the server side from another MJAPI, or\n * some other secure computing environment where the key can be kept secure.\n */\n get MJAPIKey(): string { return this.Data.MJAPIKey }\n set MJAPIKey(key: string) { this.Data.MJAPIKey = key }\n\n /**\n * This optional parameter is used when authenticating with a MemberJunction user API key (format: mj_sk_*).\n * When provided, it will be sent in the X-API-Key header. This authenticates as the specific user who owns the API key.\n *\n * Unlike MJAPIKey (system key), this is a user-specific key that can be used for automated access on behalf of a user.\n * Use this when you want to make API calls as a specific user without requiring OAuth authentication.\n */\n get UserAPIKey(): string { return this.Data.UserAPIKey }\n set UserAPIKey(key: string) { this.Data.UserAPIKey = key }\n\n /**\n * URL is the URL to the GraphQL endpoint\n */\n get URL(): string { return this.Data.URL }\n /**\n * WSURL is the URL to the GraphQL websocket endpoint. This is used for subscriptions, if you are not using subscriptions, you can pass in a blank string for this\n */\n get WSURL(): string { return this.Data.WSURL }\n\n /**\n * RefreshTokenFunction is a function that can be called by the GraphQLDataProvider whenever it receives an exception that the JWT it has already is expired\n */\n get RefreshTokenFunction(): RefreshTokenFunction { return this.Data.RefreshFunction }\n\n\n /**\n *\n * @param token Token is the JWT token that is used to authenticate the user with the server\n * @param url the URL to the GraphQL endpoint\n * @param wsurl the URL to the GraphQL websocket endpoint. This is used for subscriptions, if you are not using subscriptions, you can pass in a blank string for this\n * @param refreshTokenFunction is a function that can be called by the GraphQLDataProvider whenever it receives an exception that the JWT it has already is expired\n * @param MJCoreSchemaName the name of the MJ Core schema, if it is not the default name of __mj\n * @param includeSchemas optional, an array of schema names to include in the metadata. If not passed, all schemas are included\n * @param excludeSchemas optional, an array of schema names to exclude from the metadata. If not passed, no schemas are excluded\n * @param mjAPIKey optional, a shared secret key that is static and provided by the publisher of the MJAPI server.\n * @param userAPIKey optional, a user-specific API key (mj_sk_* format) for authenticating as a specific user\n */\n constructor(token: string,\n url: string,\n wsurl: string,\n refreshTokenFunction: RefreshTokenFunction,\n MJCoreSchemaName?: string,\n includeSchemas?: string[],\n excludeSchemas?: string[],\n mjAPIKey?: string,\n userAPIKey?: string) {\n super(\n {\n Token: token,\n URL: url,\n WSURL: wsurl,\n MJAPIKey: mjAPIKey,\n UserAPIKey: userAPIKey,\n RefreshTokenFunction: refreshTokenFunction,\n },\n MJCoreSchemaName,\n includeSchemas,\n excludeSchemas\n );\n }\n}\n\n\n\n// The GraphQLDataProvider implements both the IEntityDataProvider and IMetadataProvider interfaces.\n/**\n * The GraphQLDataProvider class is a data provider for MemberJunction that implements the IEntityDataProvider, IMetadataProvider, IRunViewProvider, IRunReportProvider, IRunQueryProvider interfaces and connects to the\n * MJAPI server using GraphQL. This class is used to interact with the server to get and save data, as well as to get metadata about the entities and fields in the system.\n */\nexport class GraphQLDataProvider extends ProviderBase implements IEntityDataProvider, IMetadataProvider, IRunReportProvider {\n private static _instance: GraphQLDataProvider;\n public static get Instance(): GraphQLDataProvider {\n return GraphQLDataProvider._instance;\n }\n\n constructor() {\n super();\n if (!GraphQLDataProvider._instance)\n GraphQLDataProvider._instance = this;\n }\n\n private _client: GraphQLClient;\n private _configData: GraphQLProviderConfigData;\n private _sessionId: string;\n private _aiClient: GraphQLAIClient;\n private _refreshPromise: Promise<void> | null = null;\n\n public get ConfigData(): GraphQLProviderConfigData {\n return this._configData;\n }\n\n\n /**\n * Gets the AI client for executing AI operations through GraphQL.\n * The client is lazily initialized on first access.\n * @returns The GraphQLAIClient instance\n */\n public get AI(): GraphQLAIClient {\n if (!this._aiClient) {\n this._aiClient = new GraphQLAIClient(this);\n }\n return this._aiClient;\n }\n\n /**\n * This getter is not implemented for the GraphQLDataProvider class.\n */\n public get DatabaseConnection(): any {\n throw new Error(\"DatabaseConnection not implemented for the GraphQLDataProvider\");\n }\n\n /**\n * The connection string for each GraphQLProvider instance is simply the URL for the GraphQL endpoint. This is because each GraphQLDataProvider instance can be configured with a different URL and each URL\n * is a unique combination of host/port/etc.\n */\n public get InstanceConnectionString(): string {\n return this._configData.URL\n }\n\n public GenerateUUID() {\n return uuidv4();\n }\n\n /**\n * The GraphQLDataProvider uses a prefix for local storage that is equal to the URL of the GraphQL endpoint. This is because the GraphQLDataProvider can be configured multiple times with different URLs and each\n * configuration will have its own local storage. This is useful when you want to have multiple connections to different servers and you don't want the local storage to be shared between them. The URL is \n * normalized to remove special characters and replace anything other than alphanumeric characters with an underscore.\n */\n protected override get LocalStoragePrefix(): string {\n if (this._configData === undefined || this._configData.URL === undefined) {\n throw new Error(\"GraphQLDataProvider: ConfigData is not set. Please call Config() first.\");\n }\n\n const replacementString = this._configData.URL.replace(/[^a-zA-Z0-9]/g, '_');\n return replacementString + \".\"; // add a period at the end to separate the prefix from the key\n }\n\n /**\n * Retrieves the stored session ID from the LocalStorageProvider if available.\n * If no session ID is found, returns null.\n * The session ID is stored using the same storage mechanism as other persistent data\n * with a key specific to the current URL to ensure uniqueness across different \n * server connections.\n * \n * @returns The stored session ID or null if not found\n */\n public async GetStoredSessionID(): Promise<string> {\n try {\n const ls = this.LocalStorageProvider;\n if (ls) {\n const key = this.LocalStoragePrefix + \"sessionId\";\n const storedSession = await ls.GetItem(key);\n return storedSession;\n }\n return null;\n } catch (e) {\n // If any error occurs, return null\n console.error(\"Error retrieving session ID from local storage:\", e);\n return null;\n }\n }\n\n /**\n * Stores the session ID using the configured LocalStorageProvider for persistence.\n * Uses the same URL-specific key pattern as other storage methods to ensure\n * proper isolation between different server connections.\n * \n * @param sessionId The session ID to store\n */\n private async SaveStoredSessionID(sessionId: string): Promise<void> {\n try {\n const ls = this.LocalStorageProvider;\n if (ls) {\n const key = this.LocalStoragePrefix + \"sessionId\";\n await ls.SetItem(key, sessionId);\n }\n } catch (e) {\n // Silently fail if storage is not available\n }\n }\n\n public async GetPreferredUUID(forceRefreshSessionId?: boolean): Promise<string> {\n // Try to get the stored session ID\n const oldUUID = await this.GetStoredSessionID();\n const UUID = forceRefreshSessionId || !oldUUID ? this.GenerateUUID() : oldUUID;\n return UUID;\n }\n\n\n /**\n * This method configures the class instance. If separateConnection is false or not provided, the global/static variables are set that means that the Config() call\n * will affect all callers to the GraphQLDataProvider including via wrappers like the Metadata class. If separateConnection is true, then the instance variables are set\n * and only this instance of the GraphQLDataProvider will be affected by the Config() call.\n * @important If separateConnection is true, metadata for the provider will be loaded but will NOT affect the Metadata class/singleton. \n * This is because the Metadata class is a singleton that binds to the first Config() call in the process where separateConnection is falsy. \n * @param configData \n * @param separateConnection \n * @returns \n */\n public async Config(configData: GraphQLProviderConfigData, providerToUse?: IMetadataProvider, separateConnection?: boolean, forceRefreshSessionId?: boolean): Promise<boolean> {\n try {\n // Enhanced logging to diagnose token issues\n // const tokenPreview = configData.Token ? `${configData.Token.substring(0, 20)}...${configData.Token.substring(configData.Token.length - 10)}` : 'NO TOKEN';\n // console.log('[GraphQL] Config called with token:', {\n // tokenPreview,\n // tokenLength: configData.Token?.length,\n // separateConnection,\n // hasRefreshFunction: !!configData.Data?.RefreshTokenFunction\n // });\n\n // CRITICAL: Always set this instance's _configData first\n // This ensures BuildDatasetFilterFromConfig() can access ConfigData.IncludeSchemas\n this._configData = configData;\n\n if (separateConnection) {\n // Get UUID after setting the configData, so that it can be used to get any stored session ID\n this._sessionId = await this.GetPreferredUUID(forceRefreshSessionId);;\n\n this._client = this.CreateNewGraphQLClient(configData.URL, configData.Token, this._sessionId, configData.MJAPIKey, configData.UserAPIKey);\n // Store the session ID for this connection\n await this.SaveStoredSessionID(this._sessionId);\n }\n else {\n // Update the singleton instance\n GraphQLDataProvider.Instance._configData = configData;\n\n if (GraphQLDataProvider.Instance._sessionId === undefined) {\n GraphQLDataProvider.Instance._sessionId = await this.GetPreferredUUID(forceRefreshSessionId);;\n }\n\n // now create the new client, if it isn't already created\n if (!GraphQLDataProvider.Instance._client)\n GraphQLDataProvider.Instance._client = this.CreateNewGraphQLClient(configData.URL, configData.Token, GraphQLDataProvider.Instance._sessionId, configData.MJAPIKey, configData.UserAPIKey);\n\n // Store the session ID for the global instance\n await GraphQLDataProvider.Instance.SaveStoredSessionID(GraphQLDataProvider.Instance._sessionId);\n\n // CRITICAL: Sync this instance with the singleton\n // This ensures ExecuteGQL() can use this._client.request()\n this._sessionId = GraphQLDataProvider.Instance._sessionId;\n this._client = GraphQLDataProvider.Instance._client;\n }\n return super.Config(configData); // now parent class can do it's config\n }\n catch (e) {\n LogError(e);\n throw (e)\n }\n }\n\n public get sessionId(): string {\n return this._sessionId;\n }\n\n protected get AllowRefresh(): boolean {\n return true; // this provider doesn't have any issues with allowing refreshes at any time\n }\n\n protected async GetCurrentUser(): Promise<UserInfo> {\n const d = await this.ExecuteGQL(this._currentUserQuery, null);\n if (d) {\n // convert the user and the user roles _mj__*** fields back to __mj_***\n const u = this.ConvertBackToMJFields(d.CurrentUser);\n const roles = u.MJUserRoles_UserIDArray.map(r => this.ConvertBackToMJFields(r));\n u.MJUserRoles_UserIDArray = roles;\n return new UserInfo(this, {...u, UserRoles: roles}) // need to pass in the UserRoles as a separate property that is what is expected here\n }\n }\n\n\n /**************************************************************************/\n // START ---- IRunReportProvider\n /**************************************************************************/\n public async RunReport(params: RunReportParams, contextUser?: UserInfo): Promise<RunReportResult> {\n const query = gql`\n query GetReportDataQuery ($ReportID: String!) {\n GetReportData(ReportID: $ReportID) {\n Success\n Results\n RowCount\n ExecutionTime\n ErrorMessage\n }\n }`\n\n const result = await this.ExecuteGQL(query, {ReportID: params.ReportID} );\n if (result && result.GetReportData)\n return {\n ReportID: params.ReportID,\n Success: result.GetReportData.Success,\n Results: JSON.parse(result.GetReportData.Results),\n RowCount: result.GetReportData.RowCount,\n ExecutionTime: result.GetReportData.ExecutionTime,\n ErrorMessage: result.GetReportData.ErrorMessage,\n };\n }\n /**************************************************************************/\n // END ---- IRunReportProvider\n /**************************************************************************/\n\n /**************************************************************************/\n // START ---- IRunQueryProvider\n /**************************************************************************/\n protected async InternalRunQuery(params: RunQueryParams, contextUser?: UserInfo): Promise<RunQueryResult> {\n // This is the internal implementation - pre/post processing is handled by ProviderBase.RunQuery()\n if (params.SQL) {\n return this.RunAdhocQuery(params.SQL, params.MaxRows);\n }\n else if (params.QueryID) {\n return this.RunQueryByID(params.QueryID, params.CategoryID, params.CategoryPath, contextUser, params.Parameters, params.MaxRows, params.StartRow);\n }\n else if (params.QueryName) {\n return this.RunQueryByName(params.QueryName, params.CategoryID, params.CategoryPath, contextUser, params.Parameters, params.MaxRows, params.StartRow);\n }\n else {\n throw new Error(\"No SQL, QueryID, or QueryName provided to RunQuery\");\n }\n }\n\n /**\n * Executes an ad-hoc SQL query via the ExecuteAdhocQuery GraphQL resolver.\n * The server validates the SQL (SELECT/WITH only) and executes on a read-only connection.\n */\n protected async RunAdhocQuery(sql: string, maxRows?: number, timeoutSeconds?: number): Promise<RunQueryResult> {\n const query = gql`\n query ExecuteAdhocQuery($input: AdhocQueryInput!) {\n ExecuteAdhocQuery(input: $input) {\n ${this.QueryReturnFieldList}\n }\n }\n `;\n\n const input: { SQL: string; TimeoutSeconds?: number } = { SQL: sql };\n if (timeoutSeconds !== undefined) {\n input.TimeoutSeconds = timeoutSeconds;\n }\n\n const result = await this.ExecuteGQL(query, { input });\n if (result?.ExecuteAdhocQuery) {\n return this.TransformQueryPayload(result.ExecuteAdhocQuery);\n }\n return {\n QueryID: '',\n QueryName: 'Ad-Hoc Query',\n Success: false,\n Results: [],\n RowCount: 0,\n TotalRowCount: 0,\n ExecutionTime: 0,\n ErrorMessage: 'Ad-hoc query execution failed — no response from server'\n };\n }\n\n protected async InternalRunQueries(params: RunQueryParams[], contextUser?: UserInfo): Promise<RunQueryResult[]> {\n // This is the internal implementation - pre/post processing is handled by ProviderBase.RunQueries()\n // Make a single batch GraphQL call for efficiency (single network roundtrip)\n const query = gql`\n query RunQueriesBatch($input: [RunQueryInput!]!) {\n RunQueries(input: $input) {\n ${this.QueryReturnFieldList}\n }\n }\n `;\n\n // Convert params to the input format expected by the GraphQL resolver\n const input = params.map(p => ({\n QueryID: p.QueryID,\n QueryName: p.QueryName,\n CategoryID: p.CategoryID,\n CategoryPath: p.CategoryPath,\n Parameters: p.Parameters,\n MaxRows: p.MaxRows,\n StartRow: p.StartRow,\n ForceAuditLog: p.ForceAuditLog,\n AuditLogDescription: p.AuditLogDescription\n }));\n\n const result = await this.ExecuteGQL(query, { input });\n if (result && result.RunQueries) {\n // Transform each result in the batch\n return result.RunQueries.map((r: unknown) => this.TransformQueryPayload(r));\n }\n return [];\n }\n\n public async RunQueryByID(QueryID: string, CategoryID?: string, CategoryPath?: string, contextUser?: UserInfo, Parameters?: Record<string, any>, MaxRows?: number, StartRow?: number): Promise<RunQueryResult> {\n const query = gql`\n query GetQueryDataQuery($QueryID: String!, $CategoryID: String, $CategoryPath: String, $Parameters: JSONObject, $MaxRows: Int, $StartRow: Int) {\n GetQueryData(QueryID: $QueryID, CategoryID: $CategoryID, CategoryPath: $CategoryPath, Parameters: $Parameters, MaxRows: $MaxRows, StartRow: $StartRow) {\n ${this.QueryReturnFieldList}\n }\n }\n `;\n \n // Build the variables object, adding optional parameters if defined.\n const variables: { QueryID: string; CategoryID?: string; CategoryPath?: string; Parameters?: Record<string, any>; MaxRows?: number; StartRow?: number } = { QueryID };\n if (CategoryID !== undefined) {\n variables.CategoryID = CategoryID;\n }\n if (CategoryPath !== undefined) {\n variables.CategoryPath = CategoryPath;\n }\n if (Parameters !== undefined) {\n variables.Parameters = Parameters;\n }\n if (MaxRows !== undefined) {\n variables.MaxRows = MaxRows;\n }\n if (StartRow !== undefined) {\n variables.StartRow = StartRow;\n }\n \n const result = await this.ExecuteGQL(query, variables);\n if (result && result.GetQueryData) {\n return this.TransformQueryPayload(result.GetQueryData);\n }\n }\n \n public async RunQueryByName(QueryName: string, CategoryID?: string, CategoryPath?: string, contextUser?: UserInfo, Parameters?: Record<string, any>, MaxRows?: number, StartRow?: number): Promise<RunQueryResult> {\n const query = gql`\n query GetQueryDataByNameQuery($QueryName: String!, $CategoryID: String, $CategoryPath: String, $Parameters: JSONObject, $MaxRows: Int, $StartRow: Int) {\n GetQueryDataByName(QueryName: $QueryName, CategoryID: $CategoryID, CategoryPath: $CategoryPath, Parameters: $Parameters, MaxRows: $MaxRows, StartRow: $StartRow) {\n ${this.QueryReturnFieldList}\n }\n }\n `;\n \n // Build the variables object, adding optional parameters if defined.\n const variables: { QueryName: string; CategoryID?: string; CategoryPath?: string; Parameters?: Record<string, any>; MaxRows?: number; StartRow?: number } = { QueryName };\n if (CategoryID !== undefined) {\n variables.CategoryID = CategoryID;\n }\n if (CategoryPath !== undefined) {\n variables.CategoryPath = CategoryPath;\n }\n if (Parameters !== undefined) {\n variables.Parameters = Parameters;\n }\n if (MaxRows !== undefined) {\n variables.MaxRows = MaxRows;\n }\n if (StartRow !== undefined) {\n variables.StartRow = StartRow;\n }\n \n const result = await this.ExecuteGQL(query, variables);\n if (result && result.GetQueryDataByName) {\n return this.TransformQueryPayload(result.GetQueryDataByName);\n }\n }\n\n protected get QueryReturnFieldList(): string {\n return `\n Success\n QueryID\n QueryName\n Results\n RowCount\n TotalRowCount\n ExecutionTime\n ErrorMessage\n AppliedParameters`\n }\n protected TransformQueryPayload(data: any): RunQueryResult {\n try {\n return {\n QueryID: data.QueryID,\n QueryName: data.QueryName,\n Success: data.Success,\n Results: JSON.parse(data.Results),\n RowCount: data.RowCount,\n TotalRowCount: data.TotalRowCount,\n ExecutionTime: data.ExecutionTime,\n ErrorMessage: data.ErrorMessage,\n AppliedParameters: data.AppliedParameters ? JSON.parse(data.AppliedParameters) : undefined\n }; \n }\n catch (e) {\n LogError(`Error transforming query payload: ${e}`);\n return null;\n }\n }\n\n /**\n * RunQueriesWithCacheCheck - Smart cache validation for batch RunQueries.\n * For each query, if cacheStatus is provided, the server checks if the cache is current\n * using the Query's CacheValidationSQL. If current, returns status='current' with no data.\n * If stale, returns status='stale' with fresh data.\n *\n * @param params - Array of RunQuery requests with optional cache status\n * @param contextUser - Optional user context\n * @returns Response containing results for each query in the batch\n */\n public async RunQueriesWithCacheCheck<T = unknown>(\n params: RunQueryWithCacheCheckParams[],\n contextUser?: UserInfo\n ): Promise<RunQueriesWithCacheCheckResponse<T>> {\n try {\n // Build the GraphQL input\n const input = params.map(item => ({\n params: {\n QueryID: item.params.QueryID || null,\n QueryName: item.params.QueryName || null,\n CategoryID: item.params.CategoryID || null,\n CategoryPath: item.params.CategoryPath || null,\n Parameters: item.params.Parameters || null,\n MaxRows: item.params.MaxRows ?? null,\n StartRow: item.params.StartRow ?? null,\n ForceAuditLog: item.params.ForceAuditLog || false,\n AuditLogDescription: item.params.AuditLogDescription || null,\n },\n cacheStatus: item.cacheStatus ? {\n maxUpdatedAt: item.cacheStatus.maxUpdatedAt,\n rowCount: item.cacheStatus.rowCount,\n } : null,\n }));\n\n const query = gql`\n query RunQueriesWithCacheCheckQuery($input: [RunQueryWithCacheCheckInput!]!) {\n RunQueriesWithCacheCheck(input: $input) {\n success\n errorMessage\n results {\n queryIndex\n queryId\n status\n Results\n maxUpdatedAt\n rowCount\n errorMessage\n }\n }\n }\n `;\n\n const responseData = await this.ExecuteGQL(query, { input });\n const response = responseData?.['RunQueriesWithCacheCheck'] as {\n success: boolean;\n errorMessage?: string;\n results: Array<{\n queryIndex: number;\n queryId: string;\n status: string;\n Results?: string;\n maxUpdatedAt?: string;\n rowCount?: number;\n errorMessage?: string;\n }>;\n };\n\n if (!response) {\n return {\n success: false,\n results: [],\n errorMessage: 'No response from server',\n };\n }\n\n // Transform results - deserialize Results for stale/no_validation results\n const transformedResults: RunQueryWithCacheCheckResult<T>[] = response.results.map(result => {\n if ((result.status === 'stale' || result.status === 'no_validation') && result.Results) {\n // Deserialize the Results JSON string\n const deserializedResults: T[] = JSON.parse(result.Results);\n\n return {\n queryIndex: result.queryIndex,\n queryId: result.queryId,\n status: result.status as 'current' | 'stale' | 'no_validation' | 'error',\n results: deserializedResults,\n maxUpdatedAt: result.maxUpdatedAt,\n rowCount: result.rowCount,\n errorMessage: result.errorMessage,\n };\n }\n\n return {\n queryIndex: result.queryIndex,\n queryId: result.queryId,\n status: result.status as 'current' | 'stale' | 'no_validation' | 'error',\n maxUpdatedAt: result.maxUpdatedAt,\n rowCount: result.rowCount,\n errorMessage: result.errorMessage,\n };\n });\n\n return {\n success: response.success,\n results: transformedResults,\n errorMessage: response.errorMessage,\n };\n } catch (e) {\n LogError(`Error in RunQueriesWithCacheCheck: ${e}`);\n return {\n success: false,\n results: [],\n errorMessage: e instanceof Error ? e.message : String(e),\n };\n }\n }\n\n /**************************************************************************/\n // END ---- IRunQueryProvider\n /**************************************************************************/\n\n\n\n /**************************************************************************/\n // START ---- IRunViewProvider\n /**************************************************************************/\n protected async InternalRunView<T = any>(params: RunViewParams, contextUser?: UserInfo): Promise<RunViewResult<T>> {\n // This is the internal implementation - pre/post processing is handled by ProviderBase.RunView()\n try {\n let qName: string = ''\n let paramType: string = ''\n if (params) {\n const innerParams: any = {}\n let entity: string, viewEntity: any;\n if (params.ViewEntity) {\n viewEntity = params.ViewEntity\n entity = viewEntity.Entity\n }\n else {\n const {entityName, v} = await this.getEntityNameAndUserView(params, contextUser)\n viewEntity = v;\n entity = entityName;\n }\n\n // get entity metadata\n const e = this.Entities.find(e => e.Name === entity);\n if (!e)\n throw new Error(`Entity ${entity} not found in metadata`);\n\n let dynamicView = false;\n const graphQLTypeName = getGraphQLTypeNameBase(e);\n\n if (params.ViewID) {\n qName = `Run${graphQLTypeName}ViewByID`;\n paramType = 'RunViewByIDInput';\n innerParams.ViewID = params.ViewID;\n }\n else if (params.ViewName) {\n qName = `Run${graphQLTypeName}ViewByName`;\n paramType = 'RunViewByNameInput';\n innerParams.ViewName = params.ViewName;\n }\n else {\n dynamicView = true;\n qName = `Run${graphQLTypeName}DynamicView`;\n paramType = 'RunDynamicViewInput';\n innerParams.EntityName = params.EntityName;\n }\n innerParams.ExtraFilter = params.ExtraFilter ? params.ExtraFilter : '';\n innerParams.OrderBy = params.OrderBy ? params.OrderBy : '';\n innerParams.UserSearchString = params.UserSearchString ? params.UserSearchString : '';\n innerParams.Fields = params.Fields; // pass it straight through, either null or array of strings\n innerParams.IgnoreMaxRows = params.IgnoreMaxRows ? params.IgnoreMaxRows : false;\n if (params.MaxRows !== undefined)\n innerParams.MaxRows = params.MaxRows;\n if (params.StartRow !== undefined)\n innerParams.StartRow = params.StartRow; // Add StartRow parameter\n innerParams.ForceAuditLog = params.ForceAuditLog ? params.ForceAuditLog : false;\n innerParams.ResultType = params.ResultType ? params.ResultType : 'simple';\n if (params.AuditLogDescription && params.AuditLogDescription.length > 0)\n innerParams.AuditLogDescription = params.AuditLogDescription;\n\n if (!dynamicView) {\n innerParams.ExcludeUserViewRunID = params.ExcludeUserViewRunID ? params.ExcludeUserViewRunID : \"\";\n innerParams.ExcludeDataFromAllPriorViewRuns = params.ExcludeDataFromAllPriorViewRuns ? params.ExcludeDataFromAllPriorViewRuns : false;\n innerParams.OverrideExcludeFilter = params.OverrideExcludeFilter ? params.OverrideExcludeFilter : '';\n innerParams.SaveViewResults = params.SaveViewResults ? params.SaveViewResults : false;\n }\n\n // Include Aggregates if provided\n if (params.Aggregates && params.Aggregates.length > 0) {\n innerParams.Aggregates = params.Aggregates.map((a: AggregateExpression) => ({\n expression: a.expression,\n alias: a.alias\n }));\n }\n\n const fieldList = this.getViewRunTimeFieldList(e, viewEntity, params, dynamicView);\n\n // Build aggregate fields for response if aggregates requested\n const aggregateResponseFields = params.Aggregates && params.Aggregates.length > 0\n ? `\n AggregateResults {\n alias\n expression\n value\n error\n }\n AggregateExecutionTime`\n : '';\n\n const query = gql`\n query RunViewQuery ($input: ${paramType}!) {\n ${qName}(input: $input) {\n Results {\n ${fieldList.join(\"\\n \")}\n }\n UserViewRunID\n RowCount\n TotalRowCount\n ExecutionTime\n Success\n ErrorMessage${aggregateResponseFields}\n }\n }`\n\n // Log aggregate request for debugging\n if (innerParams.Aggregates?.length > 0) {\n console.log('[GraphQLDataProvider] Sending RunView with aggregates:', {\n entityName: entity,\n queryName: qName,\n aggregateCount: innerParams.Aggregates.length,\n aggregates: innerParams.Aggregates\n });\n }\n\n const viewData = await this.ExecuteGQL(query, {input: innerParams} );\n if (viewData && viewData[qName]) {\n // Log aggregate response for debugging\n const responseAggregates = viewData[qName].AggregateResults;\n if (innerParams.Aggregates?.length > 0) {\n console.log('[GraphQLDataProvider] Received aggregate results:', {\n entityName: entity,\n aggregateResultCount: responseAggregates?.length || 0,\n aggregateResults: responseAggregates,\n aggregateExecutionTime: viewData[qName].AggregateExecutionTime\n });\n }\n\n // now, if we have any results in viewData that are for the CodeName, we need to convert them to the Name\n // so that the caller gets back what they expect\n const results = viewData[qName].Results;\n if (results && results.length > 0) {\n const codeNameDiffFields = e.Fields.filter(f => f.CodeName !== f.Name && f.CodeName !== undefined);\n results.forEach(r => {\n // for _mj__ results, we need to convert them back to the Name\n this.ConvertBackToMJFields(r);\n codeNameDiffFields.forEach(f => {\n r[f.Name] = r[f.CodeName];\n // delete r[f.CodeName]; // Leave the CodeName in the results, it is useful to have both\n })\n })\n }\n const result = viewData[qName];\n\n return result;\n }\n }\n else\n throw (\"No parameters passed to RunView\");\n\n return null;\n }\n catch (e) {\n LogError(e);\n throw (e)\n }\n }\n\n protected async InternalRunViews<T = any>(params: RunViewParams[], contextUser?: UserInfo): Promise<RunViewResult<T>[]> {\n\n try {\n let innerParams: any[] = [];\n let entityInfos: EntityInfo[] = [];\n let fieldList: string[] = [];\n\n for(const param of params){\n let qName: string = ''\n let paramType: string = ''\n const innerParam: any = {}\n let entity: string | null = null;\n let viewEntity: MJUserViewEntityExtended | null = null;\n if (param.ViewEntity) {\n viewEntity = param.ViewEntity as MJUserViewEntityExtended;\n entity = viewEntity.Get(\"Entity\");\n }\n else {\n const {entityName, v} = await this.getEntityNameAndUserView(param, contextUser)\n viewEntity = v;\n entity = entityName;\n }\n\n // get entity metadata\n const e = this.Entities.find(e => e.Name === entity);\n if (!e){\n throw new Error(`Entity ${entity} not found in metadata`);\n }\n\n entityInfos.push(e);\n let dynamicView: boolean = false;\n const graphQLTypeName = getGraphQLTypeNameBase(e);\n\n if (param.ViewID) {\n qName = `Run${graphQLTypeName}ViewByID`;\n paramType = 'RunViewByIDInput';\n innerParam.ViewID = param.ViewID;\n }\n else if (param.ViewName) {\n qName = `Run${graphQLTypeName}ViewByName`;\n paramType = 'RunViewByNameInput';\n innerParam.ViewName = param.ViewName;\n }\n else {\n dynamicView = true;\n qName = `Run${graphQLTypeName}DynamicView`;\n paramType = 'RunDynamicViewInput';\n innerParam.EntityName = param.EntityName;\n }\n\n innerParam.ExtraFilter = param.ExtraFilter || '';\n innerParam.OrderBy = param.OrderBy || '';\n innerParam.UserSearchString = param.UserSearchString || '';\n // pass it straight through, either null or array of strings\n innerParam.Fields = param.Fields;\n innerParam.IgnoreMaxRows = param.IgnoreMaxRows || false;\n if (param.MaxRows !== undefined)\n innerParam.MaxRows = param.MaxRows;\n if (param.StartRow !== undefined)\n innerParam.StartRow = param.StartRow; // Add StartRow parameter\n innerParam.ForceAuditLog = param.ForceAuditLog || false;\n innerParam.ResultType = param.ResultType || 'simple';\n if (param.AuditLogDescription && param.AuditLogDescription.length > 0){\n innerParam.AuditLogDescription = param.AuditLogDescription;\n }\n\n if (!dynamicView) {\n innerParam.ExcludeUserViewRunID = param.ExcludeUserViewRunID || \"\";\n innerParam.ExcludeDataFromAllPriorViewRuns = param.ExcludeDataFromAllPriorViewRuns || false;\n innerParam.OverrideExcludeFilter = param.OverrideExcludeFilter || '';\n innerParam.SaveViewResults = param.SaveViewResults || false;\n }\n\n // Include Aggregates if provided\n if (param.Aggregates && param.Aggregates.length > 0) {\n innerParam.Aggregates = param.Aggregates.map((a: AggregateExpression) => ({\n expression: a.expression,\n alias: a.alias\n }));\n }\n\n innerParams.push(innerParam);\n fieldList.push(...this.getViewRunTimeFieldList(e, viewEntity, param, dynamicView));\n }\n\n // Check if any view in the batch has aggregates\n const hasAnyAggregates = params.some(p => p.Aggregates && p.Aggregates.length > 0);\n const aggregateResponseFields = hasAnyAggregates\n ? `\n AggregateResults {\n alias\n expression\n value\n error\n }\n AggregateExecutionTime`\n : '';\n\n const query = gql`\n query RunViewsQuery ($input: [RunViewGenericInput!]!) {\n RunViews(input: $input) {\n Results {\n PrimaryKey {\n FieldName\n Value\n }\n EntityID\n Data\n }\n UserViewRunID\n RowCount\n TotalRowCount\n ExecutionTime\n Success\n ErrorMessage${aggregateResponseFields}\n }\n }`;\n\n const viewData: unknown = await this.ExecuteGQL(query, {input: innerParams} );\n if (viewData && viewData[\"RunViews\"]) {\n // now, if we have any results in viewData that are for the CodeName, we need to convert them to the Name\n // so that the caller gets back what they expect\n const results: RunViewResult[] = viewData[\"RunViews\"];\n for(const [index, result] of results.entries()){\n //const codeNameDiffFields = entityInfos[index].Fields.filter(f => f.CodeName !== f.Name && f.CodeName !== undefined);\n result.Results = result.Results.map((data: unknown) => {\n let deserializeData: Record<string, unknown> = JSON.parse(data[\"Data\"]);\n // for _mj__ results, we need to convert them back to the Name\n this.ConvertBackToMJFields(deserializeData);\n /*\n codeNameDiffFields.forEach(f => {\n deserializeData[f.Name] = deserializeData[f.CodeName];\n // delete r[f.CodeName]; // Leave the CodeName in the results, it is useful to have both\n });\n */\n return deserializeData;\n });\n }\n\n return results;\n }\n\n return null;\n\n }\n catch (e) {\n LogError(e);\n throw (e);\n }\n }\n\n /**\n * RunViewsWithCacheCheck - Smart cache validation for batch RunViews.\n * For each view, if cacheStatus is provided, the server checks if the cache is current.\n * If current, returns status='current' with no data. If stale, returns status='stale' with fresh data.\n *\n * @param params - Array of RunView requests with optional cache status\n * @param contextUser - Optional user context\n * @returns Response containing results for each view in the batch\n */\n public async RunViewsWithCacheCheck<T = unknown>(\n params: RunViewWithCacheCheckParams[],\n contextUser?: UserInfo\n ): Promise<RunViewsWithCacheCheckResponse<T>> {\n try {\n // Build the GraphQL input\n const input = params.map(item => ({\n params: {\n EntityName: item.params.EntityName || '',\n ExtraFilter: item.params.ExtraFilter || '',\n OrderBy: item.params.OrderBy || '',\n Fields: item.params.Fields,\n UserSearchString: item.params.UserSearchString || '',\n IgnoreMaxRows: item.params.IgnoreMaxRows || false,\n MaxRows: item.params.MaxRows,\n StartRow: item.params.StartRow,\n ForceAuditLog: item.params.ForceAuditLog || false,\n AuditLogDescription: item.params.AuditLogDescription || '',\n ResultType: item.params.ResultType || 'simple',\n },\n cacheStatus: item.cacheStatus ? {\n maxUpdatedAt: item.cacheStatus.maxUpdatedAt,\n rowCount: item.cacheStatus.rowCount,\n } : null,\n }));\n\n const query = gql`\n query RunViewsWithCacheCheckQuery($input: [RunViewWithCacheCheckInput!]!) {\n RunViewsWithCacheCheck(input: $input) {\n success\n errorMessage\n results {\n viewIndex\n status\n maxUpdatedAt\n rowCount\n errorMessage\n Results {\n PrimaryKey {\n FieldName\n Value\n }\n EntityID\n Data\n }\n differentialData {\n updatedRows {\n PrimaryKey {\n FieldName\n Value\n }\n EntityID\n Data\n }\n deletedRecordIDs\n }\n }\n }\n }\n `;\n\n const responseData = await this.ExecuteGQL(query, { input });\n const response = responseData?.['RunViewsWithCacheCheck'] as {\n success: boolean;\n errorMessage?: string;\n results: Array<{\n viewIndex: number;\n status: string;\n maxUpdatedAt?: string;\n rowCount?: number;\n errorMessage?: string;\n Results?: Array<{ PrimaryKey: Array<{ FieldName: string; Value: string }>; EntityID: string; Data: string }>;\n differentialData?: {\n updatedRows: Array<{ PrimaryKey: Array<{ FieldName: string; Value: string }>; EntityID: string; Data: string }>;\n deletedRecordIDs: string[];\n };\n }>;\n };\n\n if (!response) {\n return {\n success: false,\n results: [],\n errorMessage: 'No response from server',\n };\n }\n\n // Transform results - deserialize Data fields for stale/differential results\n const transformedResults: RunViewWithCacheCheckResult<T>[] = response.results.map((result, index) => {\n const inputItem = params[index];\n\n if (result.status === 'differential' && result.differentialData) {\n // Deserialize the differential data\n const deserializedUpdatedRows: T[] = result.differentialData.updatedRows.map(r => {\n const data = JSON.parse(r.Data);\n this.ConvertBackToMJFields(data);\n return data as T;\n });\n\n return {\n viewIndex: result.viewIndex,\n status: result.status as 'current' | 'stale' | 'differential' | 'error',\n results: undefined,\n differentialData: {\n updatedRows: deserializedUpdatedRows,\n deletedRecordIDs: result.differentialData.deletedRecordIDs,\n },\n maxUpdatedAt: result.maxUpdatedAt,\n rowCount: result.rowCount,\n errorMessage: result.errorMessage,\n };\n }\n\n if (result.status === 'stale' && result.Results) {\n // Deserialize the Data field and convert back MJ fields\n const deserializedResults: T[] = result.Results.map(r => {\n const data = JSON.parse(r.Data);\n this.ConvertBackToMJFields(data);\n return data as T;\n });\n\n return {\n viewIndex: result.viewIndex,\n status: result.status as 'current' | 'stale' | 'differential' | 'error',\n results: deserializedResults,\n maxUpdatedAt: result.maxUpdatedAt,\n rowCount: result.rowCount,\n errorMessage: result.errorMessage,\n };\n }\n\n return {\n viewIndex: result.viewIndex,\n status: result.status as 'current' | 'stale' | 'differential' | 'error',\n results: undefined,\n maxUpdatedAt: result.maxUpdatedAt,\n rowCount: result.rowCount,\n errorMessage: result.errorMessage,\n };\n });\n\n return {\n success: response.success,\n results: transformedResults,\n errorMessage: response.errorMessage,\n };\n } catch (e) {\n LogError(e);\n return {\n success: false,\n results: [],\n errorMessage: e instanceof Error ? e.message : String(e),\n };\n }\n }\n\n protected async getEntityNameAndUserView(params: RunViewParams, contextUser?: UserInfo): Promise<{entityName: string, v: MJUserViewEntityExtended}> {\n let entityName: string;\n let v: MJUserViewEntityExtended;\n\n if (!params.EntityName) {\n if (params.ViewID) {\n v = await ViewInfo.GetViewEntity(params.ViewID, contextUser)\n entityName = v.Entity\n }\n else if (params.ViewName) {\n v = await ViewInfo.GetViewEntityByName(params.ViewName, contextUser);\n entityName = v.Entity\n }\n else\n throw new Error(`No EntityName, ViewID or ViewName passed to RunView`)\n }\n else\n entityName = params.EntityName\n\n return {entityName, v}\n }\n\n protected getViewRunTimeFieldList(e: EntityInfo, v: MJUserViewEntityExtended, params: RunViewParams, dynamicView: boolean): string[] {\n const fieldList = [];\n const mapper = new FieldMapper();\n if (params.Fields) {\n for (const kv of e.PrimaryKeys) {\n if (params.Fields.find(f => f.trim().toLowerCase() === kv.Name.toLowerCase()) === undefined)\n fieldList.push(kv.Name); // always include the primary key fields in view run time field list\n }\n\n // now add any other fields that were passed in\n params.Fields.forEach(f => {\n fieldList.push(mapper.MapFieldName(f))\n });\n }\n else {\n // no fields were passed in. So, let's check to see if we are running an dynamic view.\n // If so, we need to include all fields since the caller didn't specify the fields they want\n // otherwise, we include the fields that are part of the view definition.\n if (dynamicView) {\n // include all fields since no fields were passed in\n e.Fields.forEach(f => {\n if (!f.IsBinaryFieldType) {\n fieldList.push(mapper.MapFieldName(f.CodeName));\n }\n });\n }\n else {\n // NOTE: in the below, c.EntityField SHOULD always exist, however there is a possibility that at some point a VIEW was created that used fields\n // and those fields are NO LONGER part of an entity, in that situation we should just remove them, rather than letting the whole view blow up which\n // would happen if we dno't check for c.EntityField? in the below\n\n // first make sure we have the primary key field in the view column list, always should, but make sure\n for (const kv of e.PrimaryKeys) {\n if (fieldList.find(f => f.trim().toLowerCase() === kv.Name.toLowerCase()) === undefined)\n fieldList.push(kv.Name); // always include the primary key fields in view run time field list\n }\n\n // Now: include the fields that are part of the view definition\n v.Columns.forEach(c => {\n if (c.hidden === false && !fieldList.find(item => item.trim().toLowerCase() === c.EntityField?.Name.trim().toLowerCase())) { // don't include hidden fields and don't include the pkey field again\n if (!c.EntityField) {\n // this can happen if a field was previously included in a view, but is no longer part of the entity\n // simply don't include it in the field list\n }\n else\n fieldList.push(mapper.MapFieldName(c.EntityField.CodeName));\n }\n });\n }\n }\n return fieldList;\n }\n /**************************************************************************/\n // END ---- IRunViewProvider\n /**************************************************************************/\n\n\n /**************************************************************************/\n // START ---- IEntityDataProvider\n /**************************************************************************/\n public get ProviderType(): ProviderType {\n return ProviderType.Network;\n }\n\n public async GetRecordChanges(entityName: string, primaryKey: CompositeKey): Promise<RecordChange[]> {\n try {\n const p: RunViewParams = {\n EntityName: 'MJ: Record Changes',\n ExtraFilter: `RecordID = '${primaryKey.Values()}' AND Entity = '${entityName}'`,\n //OrderBy: 'ChangedAt DESC',\n }\n const result = await this.RunView(p);\n if (result) {\n // sort the results client side because, for now, the RunViewParams doesn't support OrderBy dynamically like we tried. Later change this to do via the SQL query\n return result.Results.sort((a: RecordChange, b: RecordChange) => {\n return (a.ChangedAt > b.ChangedAt) ? -1 : 1 // sort descending on the date.... GraphQL passes back the date as time since base date\n });\n }\n else\n return null;\n }\n catch (e) {\n LogError(e);\n throw (e)\n }\n }\n\n\n /**\n * Returns a list of dependencies - records that are linked to the specified Entity/KeyValuePairs combination. A dependency is as defined by the relationships in the database. The MemberJunction metadata that is used\n * for this simply reflects the foreign key relationships that exist in the database. The CodeGen tool is what detects all of the relationships and generates the metadata that is used by MemberJunction. The metadata in question\n * is within the EntityField table and specifically the RelatedEntity and RelatedEntityField columns. In turn, this method uses that metadata and queries the database to determine the dependencies. To get the list of entity dependencies\n * you can use the utility method GetEntityDependencies(), which doesn't check for dependencies on a specific record, but rather gets the metadata in one shot that can be used for dependency checking.\n * @param entityName the name of the entity to check\n * @param KeyValuePairs the KeyValuePairs of the record to check\n */\n public async GetRecordDependencies(entityName: string, primaryKey: CompositeKey): Promise<RecordDependency[]> {\n try {\n // execute the gql query to get the dependencies\n const query = gql`query GetRecordDependenciesQuery ($entityName: String!, $CompositeKey: CompositeKeyInputType!) {\n GetRecordDependencies(entityName: $entityName, CompositeKey: $CompositeKey) {\n EntityName\n RelatedEntityName\n FieldName\n CompositeKey {\n KeyValuePairs {\n FieldName\n Value\n }\n }\n }\n }`\n\n // now we have our query built, execute it\n const vars = {\n entityName: entityName,\n CompositeKey: {KeyValuePairs: this.ensureKeyValuePairValueIsString(primaryKey.KeyValuePairs)}\n };\n const data = await this.ExecuteGQL(query, vars);\n\n return data?.GetRecordDependencies; // shape of the result should exactly match the RecordDependency type\n }\n catch (e) {\n LogError(e);\n throw (e)\n }\n }\n\n protected ensureKeyValuePairValueIsString(kvps: KeyValuePair[]): {FieldName: string, Value: string}[] {\n return kvps.map(kv => {\n return {FieldName: kv.FieldName, Value: kv.Value.toString()}\n })\n }\n\n public async GetRecordDuplicates(params: PotentialDuplicateRequest, contextUser?: UserInfo): Promise<PotentialDuplicateResponse>\n {\n if(!params){\n return null;\n }\n\n const query: string = gql`query GetRecordDuplicatesQuery ($params: PotentialDuplicateRequestType!) {\n GetRecordDuplicates(params: $params) {\n Status\n ErrorMessage\n PotentialDuplicateResult {\n EntityID\n DuplicateRunDetailMatchRecordIDs\n RecordPrimaryKeys {\n KeyValuePairs {\n FieldName\n Value\n }\n }\n Duplicates {\n ProbabilityScore\n KeyValuePairs {\n FieldName\n Value\n }\n }\n }\n }\n }`\n\n let request = {\n EntityID: params.EntityID,\n EntityDocumentID: params.EntityDocumentID,\n ListID: params.ListID,\n ProbabilityScore: params.ProbabilityScore,\n Options: params.Options,\n RecordIDs: params.RecordIDs.map(recordID => {\n return recordID.Copy();\n })\n }\n const data = await this.ExecuteGQL(query, {params: request});\n\n if(data && data.GetRecordDuplicates){\n return data.GetRecordDuplicates;\n }\n }\n\n public async MergeRecords(request: RecordMergeRequest, contextUser?: UserInfo, options?: EntityMergeOptions): Promise<RecordMergeResult> {\n const e = this.Entities.find(e=>e.Name.trim().toLowerCase() === request.EntityName.trim().toLowerCase());\n if (!e || !e.AllowRecordMerge)\n throw new Error(`Entity ${request.EntityName} does not allow record merging, check the AllowRecordMerge property in the entity metadata`);\n\n try {\n // execute the gql query to get the dependencies\n const mutation = gql`mutation MergeRecordsMutation ($request: RecordMergeRequest!) {\n MergeRecords(request: $request) {\n Success\n OverallStatus\n RecordMergeLogID\n RecordStatus {\n CompositeKey {\n KeyValuePairs {\n FieldName\n Value\n }\n }\n Success\n RecordMergeDeletionLogID\n Message\n }\n }\n }`\n\n // create a new request that is compatible with the server's expectations where field maps and also the primary key values are all strings\n const newRequest = {\n EntityName: request.EntityName,\n SurvivingRecordCompositeKey: {KeyValuePairs: this.ensureKeyValuePairValueIsString(request.SurvivingRecordCompositeKey.KeyValuePairs)},\n FieldMap: request.FieldMap?.map(fm => {\n return {\n FieldName: fm.FieldName,\n Value: fm.Value.toString() // turn the value into a string, since that is what the server expects\n }\n }),\n RecordsToMerge: request.RecordsToMerge.map(r => {\n return r.Copy();\n })\n }\n\n // now we have our query built, execute it\n const data = await this.ExecuteGQL(mutation, {request: newRequest});\n\n return data?.MergeRecords; // shape of the result should exactly match the RecordDependency type\n }\n catch (e) {\n LogError(e);\n return {\n Success: false,\n OverallStatus: e && e.message ? e.message : e,\n RecordStatus: [],\n RecordMergeLogID: \"\",\n Request: request,\n }\n throw (e)\n }\n }\n\n public async Save(entity: BaseEntity, user: UserInfo, options: EntitySaveOptions) : Promise<{}> {\n // IS-A parent entity save: the full ORM pipeline (permissions, validation, events)\n // already ran in BaseEntity._InnerSave(). Skip the network call — the leaf entity's\n // mutation will include all chain fields. Return current entity state.\n if (options?.IsParentEntitySave) {\n const result = new BaseEntityResult();\n result.StartedAt = new Date();\n result.EndedAt = new Date();\n result.Type = entity.IsSaved ? 'update' : 'create';\n result.Success = true;\n result.NewValues = entity.GetAll();\n entity.ResultHistory.push(result);\n return result.NewValues;\n }\n\n const result = new BaseEntityResult();\n try {\n entity.RegisterTransactionPreprocessing(); // as of the time of writing, this isn't technically needed because we are not doing any async preprocessing, but it is good to have it here for future use in case something is added with async between here and the TransactionItem being added.\n\n const vars = { input: {} };\n const type: string = entity.IsSaved ? \"Update\" : \"Create\";\n\n result.StartedAt = new Date();\n result.Type = entity.IsSaved ? 'update' : 'create';\n result.OriginalValues = entity.Fields.map(f => { return {FieldName: f.CodeName, Value: f.Value} });\n entity.ResultHistory.push(result); // push the new result as we have started a process\n\n // Create the query for the mutation first, we will provide the specific\n // input values later in the loop below. Here we are just setting up the mutation\n // and the fields that will be returned since the mutation returns back the latest\n // values for the entity and we need to have those values to update the entity after the\n // save\n\n const graphQLTypeName = getGraphQLTypeNameBase(entity.EntityInfo);\n const mutationName = `${type}${graphQLTypeName}`\n\n // only pass along writable fields, AND the PKEY value if this is an update\n const filteredFields = entity.Fields.filter(f => !f.ReadOnly || (f.IsPrimaryKey && entity.IsSaved));\n const mapper = new FieldMapper();\n const inner = ` ${mutationName}(input: $input) {\n ${entity.Fields.map(f => mapper.MapFieldName(f.CodeName)).join(\"\\n \")}\n }`\n const outer = gql`mutation ${type}${graphQLTypeName} ($input: ${mutationName}Input!) {\n ${inner}\n }\n `\n for (let i = 0; i < filteredFields.length; i++) {\n const f = filteredFields[i];\n // use entity.Get() instead of f.Value\n // in case there is an IsA relationship where parent entity \n // is where the value is. f.Value would still be old value\n let val = entity.Get(f.Name); \n if (val) {\n // type conversions as needed for GraphQL\n switch(f.EntityFieldInfo.TSType) {\n case EntityFieldTSType.Date:\n val = val.getTime();\n break;\n case EntityFieldTSType.Boolean:\n if (typeof val !== 'boolean') {\n val = parseInt(val) === 0 ? false : true; // convert to boolean\n }\n break;\n case EntityFieldTSType.Number:\n if (typeof val !== 'number') {\n const numValue = Number(val);\n if (!isNaN(numValue)) {\n val = numValue;\n } \n }\n break;\n }\n }\n\n if (val === null && f.EntityFieldInfo.AllowsNull === false) {\n // no value, field doesn't allow nulls, so set to default value, if available and then fall back to either 0 or empty string depending on type\n if (f.EntityFieldInfo.DefaultValue !== null) {\n // no value, but there is a default value, so use that, since field does NOT allow NULL\n val = f.EntityFieldInfo.DefaultValue;\n }\n else {\n // no default value, null value and field doesn't allow nulls, so set to either 0 or empty string\n if (f.FieldType === EntityFieldTSType.Number || f.FieldType === EntityFieldTSType.Boolean)\n val = 0;\n else\n val = '';\n }\n }\n vars.input[f.CodeName] = val;\n }\n\n // now add an OldValues prop to the vars IF the type === 'update' and the options.SkipOldValuesCheck === false\n if (type.trim().toLowerCase() === 'update' &&\n options.SkipOldValuesCheck === false) {\n const ov = [];\n entity.Fields.forEach(f => {\n let val = null;\n if (f.OldValue !== null && f.OldValue !== undefined) {\n if (f.EntityFieldInfo.TSType === EntityFieldTSType.Date) \n val = f.OldValue.getTime().toString();\n else if (f.EntityFieldInfo.TSType === EntityFieldTSType.Boolean)\n val = f.OldValue === true ? \"1\" : \"0\";\n else if (typeof f.OldValue !== \"string\")\n val = f.OldValue.toString();\n else\n val = f.OldValue;\n }\n ov.push({Key: f.CodeName, Value: val }); // pass ALL old values to server, slightly inefficient but we want full record\n });\n vars.input['OldValues___'] = ov; // add the OldValues prop to the input property that is part of the vars already\n }\n\n if (entity.TransactionGroup) {\n const mutationInputTypes = [\n {\n varName: 'input',\n inputType: mutationName + 'Input!'\n }\n ];\n\n entity.RaiseReadyForTransaction(); // let the entity know we're ready to be part of the transaction\n\n // we are part of a transaction group, so just add our query to the list\n // and when the transaction is committed, we will send all the queries at once\n entity.TransactionGroup.AddTransaction(new TransactionItem( entity, \n result.Type === 'create' ? 'Create' : 'Update', \n inner, vars, \n {\n mutationName,\n mutationInputTypes: mutationInputTypes\n },\n (results: any, success: boolean) => {\n // we get here whenever the transaction group does gets around to committing\n // our query. We need to update our entity with the values that were returned\n // from the mutation if it was successful.\n result.EndedAt = new Date();\n if (success && results) {\n // got our data, send it back to the caller, which is the entity object\n // and that object needs to update itself from this data.\n result.Success = true;\n result.NewValues = this.ConvertBackToMJFields(results);\n }\n else {\n // the transaction failed, nothing to update, but we need to call Reject so the\n // promise resolves with a rejection so our outer caller knows\n result.Success = false;\n result.Message = 'Transaction failed';\n }\n }));\n\n return true; // part of a TG always return true after we setup the transaction group item above\n }\n else {\n // not part of a transaction group, so just go for it and send across our GQL\n const d = await this.ExecuteGQL(outer, vars)\n if (d && d[mutationName]) {\n result.Success = true;\n result.EndedAt = new Date();\n result.NewValues = this.ConvertBackToMJFields(d[mutationName]);\n return result.NewValues;\n }\n else\n throw new Error(`Save failed for ${entity.EntityInfo.ClassName}`);\n }\n }\n catch (e) {\n result.Success = false;\n result.EndedAt = new Date();\n result.Message = e.response?.errors?.length > 0 ? e.response.errors[0].message : e.message;\n LogError(e);\n return null;\n }\n }\n public async Load(entity: BaseEntity, primaryKey: CompositeKey, EntityRelationshipsToLoad: string[] = null, user: UserInfo) : Promise<{}> {\n try {\n const vars = {};\n let pkeyInnerParamString: string = '';\n let pkeyOuterParamString: string = '';\n\n for (let i = 0; i < primaryKey.KeyValuePairs.length; i++) {\n const field: EntityFieldInfo = entity.Fields.find(f => f.Name.trim().toLowerCase() === primaryKey.KeyValuePairs[i].FieldName.trim().toLowerCase()).EntityFieldInfo;\n const val = primaryKey.GetValueByIndex(i);\n const pkeyGraphQLType: string = field.GraphQLType;\n\n // build up the param string for the outer query definition\n if (pkeyOuterParamString.length > 0)\n pkeyOuterParamString += ', ';\n pkeyOuterParamString += `$${field.CodeName}: ${pkeyGraphQLType}!`;\n\n // build up the param string for the inner query call\n if (pkeyInnerParamString.length > 0)\n pkeyInnerParamString += ', ';\n pkeyInnerParamString += `${field.CodeName}: $${field.CodeName}`;\n\n // build up the variables we are passing along to the query\n if (field.TSType === EntityFieldTSType.Number) {\n if (isNaN(primaryKey.GetValueByIndex(i)))\n throw new Error(`Primary Key value ${val} (${field.Name}) is not a valid number`);\n vars[field.CodeName] = parseInt(val); // converting to number here for graphql type to work properly\n }\n else\n vars[field.CodeName] = val;\n }\n\n const rel = EntityRelationshipsToLoad && EntityRelationshipsToLoad.length > 0 ? this.getRelatedEntityString(entity.EntityInfo, EntityRelationshipsToLoad) : '';\n\n const graphQLTypeName = getGraphQLTypeNameBase(entity.EntityInfo);\n const mapper = new FieldMapper();\n const query = gql`query Single${graphQLTypeName}${rel.length > 0 ? 'Full' : ''} (${pkeyOuterParamString}) {\n ${graphQLTypeName}(${pkeyInnerParamString}) {\n ${entity.Fields.filter((f) => !f.EntityFieldInfo.IsBinaryFieldType)\n .map((f) => {\n if (f.EntityFieldInfo.Name.trim().toLowerCase().startsWith('__mj_')) {\n // fields that start with __mj_ need to be converted to _mj__ for the GraphQL query\n return f.CodeName.replace('__mj_', '_mj__');\n } else {\n return f.CodeName;\n }\n })\n .join('\\n ')}\n ${rel}\n }\n }\n `;\n\n const d = await this.ExecuteGQL(query, vars)\n if (d && d[graphQLTypeName]) {\n // the resulting object has all the values in it, but we need to convert any elements that start with _mj__ back to __mj_\n return this.ConvertBackToMJFields(d[graphQLTypeName]);\n }\n else\n return null;\n }\n catch (e) {\n LogError(e);\n return null;\n }\n }\n\n /**\n * This method will convert back any fields that start with _mj__ back to __mj_ so that the entity object can properly update itself with the data that was returned from the server\n * @param ret\n * @returns\n */\n protected ConvertBackToMJFields(ret: any): any {\n const mapper = new FieldMapper();\n mapper.ReverseMapFields(ret);\n return ret; // clean object to pass back here\n }\n\n protected getRelatedEntityString(entityInfo: EntityInfo, EntityRelationshipsToLoad: string[]): string {\n let rel = '';\n for (let i = 0; i < entityInfo.RelatedEntities.length; i++) {\n if (EntityRelationshipsToLoad.indexOf(entityInfo.RelatedEntities[i].RelatedEntity) >= 0) {\n const r = entityInfo.RelatedEntities[i];\n const re = this.Entities.find(e => e.ID === r.RelatedEntityID);\n let uniqueCodeName: string = '';\n if (r.Type.toLowerCase().trim() === 'many to many') {\n uniqueCodeName = `${r.RelatedEntityCodeName}_${r.JoinEntityJoinField.replace(/\\s/g, '')}`;\n }\n else {\n uniqueCodeName = `${r.RelatedEntityCodeName}_${r.RelatedEntityJoinField.replace(/\\s/g, '')}`;\n }\n rel += `\n ${uniqueCodeName} {\n ${re.Fields.map(f => f.CodeName).join(\"\\n \")}\n }\n `;\n }\n }\n return rel;\n }\n\n public async Delete(entity: BaseEntity, options: EntityDeleteOptions, user: UserInfo) : Promise<boolean> {\n const result = new BaseEntityResult();\n try {\n entity.RegisterTransactionPreprocessing();\n\n result.StartedAt = new Date();\n result.Type = 'delete';\n result.OriginalValues = entity.Fields.map(f => { return {FieldName: f.CodeName, Value: f.Value} });\n entity.ResultHistory.push(result); // push the new result as we have started a process\n\n const vars = {};\n const mutationInputTypes = [];\n let pkeyInnerParamString: string = '';\n let pkeyOuterParamString: string = '';\n let returnValues: string = '';\n for (let kv of entity.PrimaryKey.KeyValuePairs) {\n const pk = entity.Fields.find(f => f.Name.trim().toLowerCase() === kv.FieldName.trim().toLowerCase()); // get the field for the primary key field\n vars[pk.CodeName] = pk.Value;\n mutationInputTypes.push({varName: pk.CodeName, inputType: pk.EntityFieldInfo.GraphQLType + '!'}); // only used when doing a transaction group, but it is easier to do in this main loop\n if (pkeyInnerParamString.length > 0)\n pkeyInnerParamString += ', ';\n pkeyInnerParamString += `${pk.CodeName}: $${pk.CodeName}`;\n\n if (pkeyOuterParamString.length > 0)\n pkeyOuterParamString += ', ';\n pkeyOuterParamString += `$${pk.CodeName}: ${pk.EntityFieldInfo.GraphQLType}!`;\n\n if (returnValues.length > 0)\n returnValues += '\\n ';\n returnValues += `${pk.CodeName}`;\n }\n\n mutationInputTypes.push({varName: \"options___\", inputType: 'DeleteOptionsInput!'}); // only used when doing a transaction group, but it is easier to do in this main loop\n\n // Build delete options ensuring all required fields are present.\n // IMPORTANT: Must be kept in sync with DeleteOptionsInput in @memberjunction/server\n // and EntityDeleteOptions in @memberjunction/core\n vars[\"options___\"] = {\n SkipEntityAIActions: options?.SkipEntityAIActions ?? false,\n SkipEntityActions: options?.SkipEntityActions ?? false,\n ReplayOnly: options?.ReplayOnly ?? false,\n IsParentEntityDelete: options?.IsParentEntityDelete ?? false\n };\n\n const graphQLTypeName = getGraphQLTypeNameBase(entity.EntityInfo);\n const queryName: string = 'Delete' + graphQLTypeName;\n const inner = gql`${queryName}(${pkeyInnerParamString}, options___: $options___) {\n ${returnValues}\n }\n `\n const query = gql`mutation ${queryName} (${pkeyOuterParamString}, $options___: DeleteOptionsInput!) {\n ${inner}\n }\n `\n\n if (entity.TransactionGroup) {\n // we have a transaction group, need to play nice and be part of it\n entity.RaiseReadyForTransaction();\n // we are part of a transaction group, so just add our query to the list\n // and when the transaction is committed, we will send all the queries at once\n entity.TransactionGroup.AddTransaction(new TransactionItem(entity, 'Delete', inner, vars, {mutationName: queryName,\n mutationInputTypes: mutationInputTypes},\n (results: any, success: boolean) => {\n // we get here whenever the transaction group does gets around to committing\n // our query.\n result.EndedAt = new Date(); // done processing\n if (success && results) {\n // success indicated by the entity.PrimaryKey.Value matching the return value of the mutation\n let success: boolean = true;\n for (const pk of entity.PrimaryKey.KeyValuePairs) {\n // check each primary key value to see if it matches the return value of the mutation\n if (pk.Value !== results[pk.FieldName]) {\n success = false;\n }\n }\n if (success) {\n result.Success = true;\n }\n else {\n // the transaction failed, nothing to update, but we need to call Reject so the\n // promise resolves with a rejection so our outer caller knows\n result.Success = false;\n result.Message = 'Transaction failed to commit'\n }\n }\n else {\n // the transaction failed, nothing to update, but we need to call Reject so the\n // promise resolves with a rejection so our outer caller knows\n result.Success = false;\n result.Message = 'Transaction failed to commit'\n }\n }));\n return true;\n }\n else {\n // no transaction just go for it\n const d = await this.ExecuteGQL(query, vars)\n if (d && d[queryName]) {\n const data = d[queryName];\n for (let key of entity.PrimaryKey.KeyValuePairs) {\n // we want to now compare key.Value against data[key.FieldName]\n let returnedVal = data[key.FieldName];\n let originalVal = key.Value;\n // we want to ignore types so we should convert numbers to strings for the comparison\n if (typeof originalVal === 'number')\n originalVal = originalVal.toString();\n if (typeof returnedVal === 'number')\n returnedVal = returnedVal.toString();\n // now compare the two values\n if (originalVal !== returnedVal) {\n throw new Error (`Primary key value mismatch in server Delete response. Field: ${key.FieldName}, Original: ${originalVal}, Returned: ${returnedVal}`);\n }\n }\n result.Success = true;\n result.EndedAt = new Date(); // done processing\n return true; // all of the return values match the primary key values, so we are good and delete worked\n }\n else\n throw new Error(`Delete failed for ${entity.EntityInfo.Name}: ${entity.PrimaryKey.ToString()} `);\n }\n }\n catch (e) {\n result.EndedAt = new Date(); // done processing\n result.Success = false;\n result.Message = e.response?.errors?.length > 0 ? e.response.errors[0].message : e.message;\n LogError(e);\n\n return false;\n }\n }\n /**************************************************************************/\n // END ---- IEntityDataProvider\n /**************************************************************************/\n\n\n /**************************************************************************/\n // START ---- IMetadataProvider\n /**************************************************************************/\n /**\n * Returns a dataset by name\n * @param datasetName \n * @param itemFilters \n * @returns \n */\n public async GetDatasetByName(datasetName: string, itemFilters?: DatasetItemFilterType[]): Promise<DatasetResultType> {\n const query = gql`query GetDatasetByName($DatasetName: String!, $ItemFilters: [DatasetItemFilterTypeGQL!]) {\n GetDatasetByName(DatasetName: $DatasetName, ItemFilters: $ItemFilters) {\n DatasetID\n DatasetName\n Success\n Status\n LatestUpdateDate\n Results\n }\n }`\n const data = await this.ExecuteGQL(query, {DatasetName: datasetName, ItemFilters: itemFilters });\n if (data && data.GetDatasetByName && data.GetDatasetByName.Success) {\n return {\n DatasetID: data.GetDatasetByName.DatasetID,\n DatasetName: data.GetDatasetByName.DatasetName,\n Success: data.GetDatasetByName.Success,\n Status: data.GetDatasetByName.Status,\n LatestUpdateDate: new Date(data.GetDatasetByName.LatestUpdateDate),\n Results: JSON.parse(data.GetDatasetByName.Results)\n }\n }\n else {\n return {\n DatasetID: \"\",\n DatasetName: datasetName,\n Success: false,\n Status: 'Unknown',\n LatestUpdateDate: null,\n Results: null\n };\n }\n }\n\n public async GetDatasetStatusByName(datasetName: string, itemFilters?: DatasetItemFilterType[]): Promise<DatasetStatusResultType> {\n const query = gql`query GetDatasetStatusByName($DatasetName: String!, $ItemFilters: [DatasetItemFilterTypeGQL!]) {\n GetDatasetStatusByName(DatasetName: $DatasetName, ItemFilters: $ItemFilters) {\n DatasetID\n DatasetName\n Success\n Status\n LatestUpdateDate\n EntityUpdateDates\n }\n }`\n const data = await this.ExecuteGQL(query, {DatasetName: datasetName, ItemFilters: itemFilters});\n if (data && data.GetDatasetStatusByName && data.GetDatasetStatusByName.Success) {\n return {\n DatasetID: data.GetDatasetStatusByName.DatasetID,\n DatasetName: data.GetDatasetStatusByName.DatasetName,\n Success: data.GetDatasetStatusByName.Success,\n Status: data.GetDatasetStatusByName.Status,\n LatestUpdateDate: new Date(data.GetDatasetStatusByName.LatestUpdateDate),\n EntityUpdateDates: JSON.parse(data.GetDatasetStatusByName.EntityUpdateDates)\n }\n }\n else {\n return {\n DatasetID: \"\",\n DatasetName: datasetName,\n Success: false,\n Status: 'Unknown',\n LatestUpdateDate: null,\n EntityUpdateDates: null\n };\n }\n }\n\n public async CreateTransactionGroup(): Promise<TransactionGroupBase> {\n return new GraphQLTransactionGroup(this);\n }\n\n public async GetRecordFavoriteStatus(userId: string, entityName: string, primaryKey: CompositeKey): Promise<boolean> {\n const valResult = primaryKey.Validate();\n if (!valResult.IsValid)\n return false;\n\n const e = this.Entities.find(e => e.Name === entityName)\n if (!e)\n throw new Error(`Entity ${entityName} not found in metadata`);\n\n const query = gql`query GetRecordFavoriteStatus($params: UserFavoriteSearchParams!) {\n GetRecordFavoriteStatus(params: $params) {\n Success\n IsFavorite\n }\n }`\n\n const data = await this.ExecuteGQL(query, {params: {\n UserID: userId,\n EntityID: e.ID,\n CompositeKey: {KeyValuePairs: this.ensureKeyValuePairValueIsString(primaryKey.KeyValuePairs)}\n }\n }\n );\n if (data && data.GetRecordFavoriteStatus && data.GetRecordFavoriteStatus.Success)\n return data.GetRecordFavoriteStatus.IsFavorite;\n }\n\n public async SetRecordFavoriteStatus(userId: string, entityName: string, primaryKey: CompositeKey, isFavorite: boolean, contextUser: UserInfo): Promise<void> {\n const e = this.Entities.find(e => e.Name === entityName)\n if (!e){\n throw new Error(`Entity ${entityName} not found in metadata`);\n }\n\n const query = gql`mutation SetRecordFavoriteStatus($params: UserFavoriteSetParams!) {\n SetRecordFavoriteStatus(params: $params){\n Success\n }\n }`\n\n const data = await this.ExecuteGQL(query, { params: {\n UserID: userId,\n EntityID: e.ID,\n CompositeKey: {KeyValuePairs: this.ensureKeyValuePairValueIsString(primaryKey.KeyValuePairs)},\n IsFavorite: isFavorite}\n }\n );\n if (data && data.SetRecordFavoriteStatus !== null)\n return data.SetRecordFavoriteStatus.Success;\n }\n\n protected async InternalGetEntityRecordName(entityName: string, primaryKey: CompositeKey): Promise<string> {\n if (!entityName || !primaryKey || primaryKey.KeyValuePairs?.length === 0){\n return null;\n }\n\n const query = gql`query GetEntityRecordNameQuery ($EntityName: String!, $CompositeKey: CompositeKeyInputType!) {\n GetEntityRecordName(EntityName: $EntityName, CompositeKey: $CompositeKey) {\n Success\n Status\n RecordName\n }\n }`\n\n const data = await this.ExecuteGQL(query, {\n EntityName: entityName,\n CompositeKey: {KeyValuePairs: this.ensureKeyValuePairValueIsString(primaryKey.KeyValuePairs)}\n });\n if (data && data.GetEntityRecordName && data.GetEntityRecordName.Success)\n return data.GetEntityRecordName.RecordName;\n }\n\n protected async InternalGetEntityRecordNames(info: EntityRecordNameInput[]): Promise<EntityRecordNameResult[]> {\n if (!info)\n return null;\n\n const query = gql`query GetEntityRecordNamesQuery ($info: [EntityRecordNameInput!]!) {\n GetEntityRecordNames(info: $info) {\n Success\n Status\n CompositeKey {\n KeyValuePairs {\n FieldName\n Value\n }\n }\n EntityName\n RecordName\n }\n }`\n\n const data = await this.ExecuteGQL(query, {info: info.map(i => {\n return {\n EntityName: i.EntityName,\n CompositeKey: {KeyValuePairs: this.ensureKeyValuePairValueIsString(i.CompositeKey.KeyValuePairs)}\n }\n })});\n if (data && data.GetEntityRecordNames) {\n // Convert plain CompositeKey objects from GraphQL response to real CompositeKey instances\n return data.GetEntityRecordNames.map((result: EntityRecordNameResult) => ({\n ...result,\n CompositeKey: new CompositeKey(result.CompositeKey.KeyValuePairs)\n }));\n }\n }\n\n /**\n * Retrieves all of the data context data for the specified data context ID.\n * @param dataContextID \n * @returns \n */\n public async GetDataContextData(dataContextID: string) {\n try {\n const query = gql`query GetDataContextData ($DataContextID: String!) {\n GetDataContextData(DataContextID: $DataContextID) {\n Success\n ErrorMessages\n Results\n }\n }`\n \n const data = await this.ExecuteGQL(query, {DataContextID: dataContextID});\n if (data && data.GetDataContextData) {\n if (data.GetDataContextData.Success) {\n return data.GetDataContextData.Results.map((item: any) => {\n return JSON.parse(item);\n });\n }\n else {\n throw new Error(data.GetDataContextData.ErrorMessages.join(', '));\n }\n }\n else {\n throw new Error('GraphQL query failed');\n } \n }\n catch (e) {\n LogError(e);\n throw e;\n }\n }\n\n /**\n * Retrieves the data context item data for the specified data context item ID.\n * @param dataContextItemID \n * @returns \n */\n public async GetDataContextItemData(dataContextItemID: string) {\n try {\n const query = gql`query GetDataContextItemData ($DataContextItemID: String!) {\n GetDataContextItemData(DataContextItemID: $DataContextItemID) {\n Success\n ErrorMessage\n Result\n }\n }`\n \n const data = await this.ExecuteGQL(query, {DataContextItemID: dataContextItemID});\n if (data && data.GetDataContextItemData) {\n if (data.GetDataContextItemData.Success) {\n return JSON.parse(data.GetDataContextItemData.Result);\n }\n else {\n throw new Error(data.GetDataContextItemData.ErrorMessage);\n }\n }\n else {\n throw new Error('GraphQL query failed');\n } \n }\n catch (e) {\n LogError(e);\n throw e;\n }\n }\n\n /**\n * Static version of the ExecuteGQL method that will use the global instance of the GraphQLDataProvider and execute the specified query with the provided variables. \n * If the token is expired, it will attempt to refresh the token and then re-execute the query. If the token is expired and the refresh fails, it will throw an error.\n * @param query \n * @param variables \n * @param refreshTokenIfNeeded \n * @returns \n */\n public static async ExecuteGQL(query: string, variables: any, refreshTokenIfNeeded: boolean = true): Promise<any> {\n return GraphQLDataProvider.Instance.ExecuteGQL(query, variables, refreshTokenIfNeeded);\n }\n\n /**\n * Executes the GQL query with the provided variables. If the token is expired, it will attempt to refresh the token and then re-execute the query. If the token is expired and the refresh fails, it will throw an error.\n * @param query \n * @param variables \n * @param refreshTokenIfNeeded \n * @returns \n */\n public async ExecuteGQL(query: string, variables: any, refreshTokenIfNeeded: boolean = true): Promise<any> {\n try {\n const data = await this._client.request(query, variables);\n return data;\n }\n catch (e) {\n // Enhanced error logging to diagnose 500 errors\n console.error('[GraphQL] ExecuteGQL error caught:', {\n hasResponse: !!e?.response,\n hasErrors: !!e?.response?.errors,\n errorCount: e?.response?.errors?.length,\n firstError: e?.response?.errors?.[0],\n errorCode: e?.response?.errors?.[0]?.extensions?.code,\n errorMessage: e?.response?.errors?.[0]?.message,\n fullError: e\n });\n\n if (e && e.response && e.response.errors?.length > 0) {//e.code === 'JWT_EXPIRED') {\n const error = e.response.errors[0];\n const code = error?.extensions?.code?.toUpperCase().trim()\n if (code === 'JWT_EXPIRED') {\n if (refreshTokenIfNeeded) {\n // token expired, so we need to refresh it and try again\n await this.RefreshToken();\n return await this.ExecuteGQL(query, variables, false/*don't attempt to refresh again*/);\n }\n else {\n // token expired but the caller doesn't want a refresh, so just return the error\n LogError(`JWT_EXPIRED and refreshTokenIfNeeded is false`);\n throw e;\n }\n }\n else\n throw e;\n }\n else {\n LogError(e);\n throw e; // force the caller to handle the error\n }\n }\n }\n\n public async RefreshToken(): Promise<void> {\n // Check if we're in singleton mode\n const isInstanceSingleton = GraphQLDataProvider.Instance &&\n GraphQLDataProvider.Instance._configData === this._configData;\n\n // If singleton has a refresh in progress, wait for it\n if (isInstanceSingleton && GraphQLDataProvider.Instance._refreshPromise) {\n return GraphQLDataProvider.Instance._refreshPromise;\n }\n\n // If this instance has a refresh in progress, wait for it\n if (this._refreshPromise) {\n return this._refreshPromise;\n }\n\n // Start a new refresh\n console.log('[GraphQL] Starting token refresh...');\n this._refreshPromise = this.performTokenRefresh();\n\n // Also store on singleton if in singleton mode\n if (isInstanceSingleton) {\n GraphQLDataProvider.Instance._refreshPromise = this._refreshPromise;\n }\n\n try {\n await this._refreshPromise;\n console.log('[GraphQL] Token refresh completed successfully');\n } finally {\n // Clear the promise when done\n this._refreshPromise = null;\n if (isInstanceSingleton && GraphQLDataProvider.Instance) {\n GraphQLDataProvider.Instance._refreshPromise = null;\n }\n }\n }\n\n private async performTokenRefresh(): Promise<void> {\n if (this._configData.Data.RefreshTokenFunction) {\n const newToken = await this._configData.Data.RefreshTokenFunction();\n if (newToken) {\n this._configData.Token = newToken; // update the token\n const newClient = this.CreateNewGraphQLClient(this._configData.URL,\n this._configData.Token,\n this._sessionId,\n this._configData.MJAPIKey,\n this._configData.UserAPIKey);\n\n // Update this instance's client\n this._client = newClient;\n\n // CRITICAL: Also update the singleton's client if we're in singleton mode\n // Check if this._configData is the same reference as Instance._configData\n if (GraphQLDataProvider.Instance &&\n GraphQLDataProvider.Instance._configData === this._configData) {\n GraphQLDataProvider.Instance._client = newClient;\n }\n }\n else {\n throw new Error('Refresh token function returned null or undefined token');\n }\n }\n else {\n throw new Error('No refresh token function provided');\n }\n }\n\n public static async RefreshToken(): Promise<void> {\n return GraphQLDataProvider.Instance.RefreshToken();\n }\n\n protected CreateNewGraphQLClient(url: string, token: string, sessionId: string, mjAPIKey: string, userAPIKey?: string): GraphQLClient {\n // Enhanced logging to diagnose token issues\n // const tokenPreview = token ? `${token.substring(0, 20)}...${token.substring(token.length - 10)}` : 'NO TOKEN';\n // console.log('[GraphQL] Creating new client:', {\n // url,\n // tokenPreview,\n // tokenLength: token?.length,\n // sessionId,\n // hasMJAPIKey: !!mjAPIKey,\n // hasUserAPIKey: !!userAPIKey\n // });\n\n const headers: Record<string, string> = {\n 'x-session-id': sessionId,\n };\n if (token)\n headers.authorization = 'Bearer ' + token;\n if (mjAPIKey)\n headers['x-mj-api-key'] = mjAPIKey;\n if (userAPIKey)\n headers['x-api-key'] = userAPIKey;\n\n return new GraphQLClient(url, {\n headers\n });\n }\n\n private _innerCurrentUserQueryString = `CurrentUser {\n ${this.userInfoString()}\n MJUserRoles_UserIDArray {\n ${this.userRoleInfoString()}\n }\n }\n `\n\n\n private _currentUserQuery = gql`query CurrentUserAndRoles {\n ${this._innerCurrentUserQueryString}\n }`\n\n\n\n private userInfoString(): string {\n return this.infoString(new UserInfo(null, null))\n }\n private userRoleInfoString(): string {\n return this.infoString(new UserRoleInfo(null))\n }\n private infoString(object: any): string {\n let sOutput: string = '';\n const keys = Object.keys(object)\n for (const k of keys) {\n if (k.startsWith('__mj_')) {\n sOutput += k.replace('__mj_', '_mj__') + '\\n '\n }\n else if (!k.startsWith('_')) {\n sOutput += k + '\\n '\n }\n }\n return sOutput\n }\n\n\n private _localStorageProvider: ILocalStorageProvider;\n get LocalStorageProvider(): ILocalStorageProvider {\n if (!this._localStorageProvider) {\n // Use BrowserIndexedDBStorageProvider in browser environments where indexedDB is available,\n // otherwise fall back to InMemoryLocalStorageProvider for Node.js/server environments\n if (typeof indexedDB !== 'undefined') {\n this._localStorageProvider = new BrowserIndexedDBStorageProvider();\n } else {\n this._localStorageProvider = new InMemoryLocalStorageProvider();\n }\n }\n\n return this._localStorageProvider;\n }\n\n\n /**************************************************************************/\n // END ---- IMetadataProvider\n /**************************************************************************/\n protected get Metadata(): IMetadataProvider {\n return this;\n }\n\n private _wsClient: Client = null;\n private _wsClientCreatedAt: number = null;\n private _pushStatusSubjects: Map<string, {\n subject: Subject<string>,\n subscription: Subscription,\n createdAt: number,\n lastRequestedAt: number,\n lastEmissionAt: number,\n activeSubscribers: number\n }> = new Map();\n // Tracks total WebSocket subscriptions (not component subscribers)\n // Used to prevent disposing client when subscriptions are active\n private _activeSubscriptionCount = 0;\n private readonly WS_CLIENT_MAX_AGE_MS = 30 * 60 * 1000; // 30 minutes\n private readonly SUBSCRIPTION_CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes\n private readonly SUBSCRIPTION_IDLE_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes\n private _subscriptionCleanupTimer: NodeJS.Timeout = null;\n private _isCleaningUp = false; // Prevent concurrent cleanup\n\n /**\n * Gets or creates a WebSocket client with health checking and automatic cleanup\n * @returns Active WebSocket client\n */\n private getOrCreateWSClient(): Client {\n const now = Date.now();\n\n // Check if existing client is too old and should be recreated\n if (this._wsClient && this._wsClientCreatedAt) {\n const age = now - this._wsClientCreatedAt;\n if (age > this.WS_CLIENT_MAX_AGE_MS && this._activeSubscriptionCount === 0) {\n // Client is old and no active subscriptions, recreate it\n this.disposeWSClient();\n }\n }\n\n // Create new client if needed\n if (!this._wsClient) {\n this._wsClient = createClient({\n url: this.ConfigData.WSURL,\n connectionParams: {\n Authorization: 'Bearer ' + this.ConfigData.Token,\n },\n keepAlive: 30000, // Send keepalive ping every 30 seconds\n retryAttempts: 3,\n shouldRetry: () => true,\n });\n this._wsClientCreatedAt = now;\n\n // Start cleanup timer if not already running\n if (!this._subscriptionCleanupTimer) {\n this._subscriptionCleanupTimer = setInterval(() => {\n this.cleanupStaleSubscriptions();\n }, this.SUBSCRIPTION_CLEANUP_INTERVAL_MS);\n }\n }\n\n return this._wsClient;\n }\n\n /**\n * Disposes of the WebSocket client\n * Does NOT complete subjects - caller should handle that separately to avoid double-cleanup\n */\n private disposeWSClient(): void {\n if (this._wsClient) {\n try {\n this._wsClient.dispose();\n } catch (e) {\n console.error('[GraphQLDataProvider] Error disposing WebSocket client:', e);\n }\n this._wsClient = null;\n this._wsClientCreatedAt = null;\n }\n }\n\n /**\n * Completes all subjects and clears the cache\n * Separate from disposeWSClient to avoid double-cleanup\n */\n private completeAllSubjects(): void {\n this._pushStatusSubjects.forEach((entry, sessionId) => {\n try {\n entry.subject.complete();\n entry.subscription.unsubscribe();\n } catch (e) {\n console.error(`[GraphQLDataProvider] Error cleaning up subject for ${sessionId}:`, e);\n }\n });\n this._pushStatusSubjects.clear();\n }\n\n /**\n * Cleans up stale subscription subjects that haven't been used recently\n * Uses cleanup flag to prevent concurrent execution\n * Uses Map snapshot to avoid concurrent modification issues\n */\n private cleanupStaleSubscriptions(): void {\n // Prevent concurrent cleanup\n if (this._isCleaningUp) {\n return;\n }\n this._isCleaningUp = true;\n\n try {\n const now = Date.now();\n const initialCount = this._pushStatusSubjects.size;\n\n // Create snapshot to avoid concurrent modification during iteration\n const entries = Array.from(this._pushStatusSubjects.entries());\n const toRemove: string[] = [];\n\n // Identify stale subscriptions (must have no active subscribers AND be idle)\n entries.forEach(([sessionId, value]) => {\n const timeSinceRequested = now - value.lastRequestedAt;\n const timeSinceEmission = now - value.lastEmissionAt;\n\n // Clean up if ALL conditions are true:\n // 1. No active subscribers (no component is listening)\n // 2. Not requested recently (no component has requested it)\n // 3. Not receiving data (no active server communication)\n const shouldCleanup = value.activeSubscribers === 0 &&\n timeSinceRequested >= this.SUBSCRIPTION_IDLE_TIMEOUT_MS &&\n timeSinceEmission >= this.SUBSCRIPTION_IDLE_TIMEOUT_MS;\n\n if (shouldCleanup) {\n console.log(`[GraphQLDataProvider] Marking session ${sessionId} for cleanup: ` +\n `activeSubscribers=${value.activeSubscribers}, ` +\n `timeSinceRequested=${Math.round(timeSinceRequested/1000)}s, ` +\n `timeSinceEmission=${Math.round(timeSinceEmission/1000)}s`);\n toRemove.push(sessionId);\n }\n });\n\n // Complete subjects and unsubscribe from WebSocket\n toRemove.forEach(sessionId => {\n const entry = this._pushStatusSubjects.get(sessionId);\n if (entry) {\n try {\n entry.subject.complete(); // Completes for ALL subscribers\n entry.subscription.unsubscribe(); // Closes WebSocket subscription\n this._pushStatusSubjects.delete(sessionId);\n console.log(`[GraphQLDataProvider] Cleaned up stale subscription for session: ${sessionId}`);\n } catch (e) {\n console.error(`[GraphQLDataProvider] Error cleaning up subscription for ${sessionId}:`, e);\n }\n }\n });\n\n if (toRemove.length > 0) {\n console.log(`[GraphQLDataProvider] Cleaned up ${toRemove.length} stale subscription(s)`);\n }\n\n // If no subscriptions remain and client is old, dispose of it\n if (this._pushStatusSubjects.size === 0 && this._wsClient && this._wsClientCreatedAt) {\n const clientAge = now - this._wsClientCreatedAt;\n if (clientAge > this.WS_CLIENT_MAX_AGE_MS) {\n console.log('[GraphQLDataProvider] Disposing of idle WebSocket client');\n this.disposeWSClient();\n }\n }\n } finally {\n this._isCleaningUp = false;\n }\n }\n\n /**\n * Generic subscription method for GraphQL subscriptions\n * @param subscription The GraphQL subscription query\n * @param variables Variables to pass to the subscription\n * @returns Observable that emits subscription data\n */\n public subscribe(subscription: string, variables?: any): Observable<any> {\n return new Observable((observer) => {\n const client = this.getOrCreateWSClient();\n this._activeSubscriptionCount++;\n\n const unsubscribe = client.subscribe(\n { query: subscription, variables },\n {\n next: (data) => {\n observer.next(data.data);\n },\n error: async (error: unknown) => {\n // Check if error is JWT_EXPIRED\n const errorObj = error as { extensions?: { code?: string }, message?: string };\n const isTokenExpired =\n errorObj?.extensions?.code === 'JWT_EXPIRED' ||\n errorObj?.message?.includes('token has expired') ||\n errorObj?.message?.includes('JWT_EXPIRED');\n\n if (isTokenExpired) {\n console.log('[GraphQLDataProvider] WebSocket JWT token expired, refreshing and reconnecting...');\n try {\n // Refresh the token\n await this.RefreshToken();\n\n // Dispose old WebSocket client\n this.disposeWSClient();\n\n // Observer will be completed, and caller should re-subscribe\n // which will create a new WebSocket connection with the new token\n observer.complete();\n } catch (refreshError) {\n console.error('[GraphQLDataProvider] Failed to refresh token for WebSocket:', refreshError);\n observer.error(refreshError);\n }\n } else {\n observer.error(error);\n }\n },\n complete: () => {\n observer.complete();\n },\n }\n );\n\n // Return cleanup function - this is ALWAYS called when subscription ends\n // whether by error, completion, or manual unsubscribe\n return () => {\n this._activeSubscriptionCount--;\n unsubscribe();\n };\n });\n }\n\n public PushStatusUpdates(sessionId: string = null): Observable<string> {\n if (!sessionId)\n sessionId = this.sessionId;\n\n const now = Date.now();\n\n // Check for existing subject\n const existing = this._pushStatusSubjects.get(sessionId);\n if (existing) {\n // Update last requested time\n existing.lastRequestedAt = now;\n // Wrap with defer to increment on subscribe and finalize to decrement on unsubscribe\n return new Observable<string>((observer) => {\n // Increment subscriber count when Observable is subscribed to\n existing.activeSubscribers++;\n\n // Subscribe to the underlying Subject\n const subscription = existing.subject.subscribe(observer);\n\n // Return teardown function that decrements count\n return () => {\n const entry = this._pushStatusSubjects.get(sessionId);\n if (entry && entry.activeSubscribers > 0) {\n entry.activeSubscribers--;\n }\n subscription.unsubscribe();\n };\n });\n }\n\n const SUBSCRIBE_TO_STATUS = gql`subscription StatusUpdates($sessionId: String!) {\n statusUpdates(sessionId: $sessionId) {\n date\n message\n sessionId\n }\n }\n `;\n\n // Create new Subject for status updates (no buffering - status updates are ephemeral)\n const subject = new Subject<string>();\n const client = this.getOrCreateWSClient();\n\n // Subscribe to WebSocket and pipe data to Subject\n const subscription = new Subscription();\n subscription.add(\n new Observable((observer) => {\n const unsubscribe = client.subscribe(\n { query: SUBSCRIBE_TO_STATUS, variables: { sessionId } },\n {\n next: (data: any) => {\n // Update last emission time\n const entry = this._pushStatusSubjects.get(sessionId);\n if (entry) {\n entry.lastEmissionAt = Date.now();\n }\n // Extract the message and emit to subject\n observer.next(data.data.statusUpdates.message);\n },\n error: async (error: unknown) => {\n // Check if error is JWT_EXPIRED\n const errorObj = error as { extensions?: { code?: string }, message?: string };\n const isTokenExpired =\n errorObj?.extensions?.code === 'JWT_EXPIRED' ||\n errorObj?.message?.includes('token has expired') ||\n errorObj?.message?.includes('JWT_EXPIRED');\n\n if (isTokenExpired) {\n console.log('[GraphQLDataProvider] PushStatusUpdates JWT token expired, refreshing and reconnecting...');\n try {\n // Refresh the token\n await this.RefreshToken();\n\n // Dispose old WebSocket client\n this.disposeWSClient();\n\n // Complete the subscription - components will auto-reconnect via RxJS retry logic\n observer.complete();\n } catch (refreshError) {\n console.error('[GraphQLDataProvider] Failed to refresh token for PushStatusUpdates:', refreshError);\n observer.error(refreshError);\n }\n } else {\n observer.error(error);\n }\n },\n complete: () => {\n observer.complete();\n },\n }\n );\n\n // Increment AFTER successful subscription setup\n this._activeSubscriptionCount++;\n\n return () => {\n this._activeSubscriptionCount--;\n unsubscribe();\n };\n }).subscribe({\n next: (message: string) => subject.next(message),\n error: (error) => {\n // On error, complete subject and remove from cache\n subject.error(error);\n this._pushStatusSubjects.delete(sessionId);\n },\n complete: () => {\n // On completion, complete subject and remove from cache\n subject.complete();\n this._pushStatusSubjects.delete(sessionId);\n }\n })\n );\n\n // Store subject with tracking data\n this._pushStatusSubjects.set(sessionId, {\n subject,\n subscription,\n createdAt: now,\n lastRequestedAt: now,\n lastEmissionAt: now,\n activeSubscribers: 0 // Will be incremented when first component subscribes\n });\n\n // Wrap return Observable to track subscribers\n return new Observable<string>((observer) => {\n // Increment subscriber count when Observable is subscribed to\n const entry = this._pushStatusSubjects.get(sessionId);\n if (entry) {\n entry.activeSubscribers++;\n }\n\n // Subscribe to the underlying Subject\n const sub = subject.subscribe(observer);\n\n // Return teardown function that decrements count\n return () => {\n const entry = this._pushStatusSubjects.get(sessionId);\n if (entry && entry.activeSubscribers > 0) {\n entry.activeSubscribers--;\n }\n sub.unsubscribe();\n };\n });\n }\n\n /**\n * Public method to dispose of WebSocket resources\n * Call this when shutting down the provider or on logout\n */\n public disposeWebSocketResources(): void {\n // Stop cleanup timer\n if (this._subscriptionCleanupTimer) {\n clearInterval(this._subscriptionCleanupTimer);\n this._subscriptionCleanupTimer = null;\n }\n\n // Complete all subjects and clear cache\n this.completeAllSubjects();\n\n // Reset counters\n this._activeSubscriptionCount = 0;\n\n // Dispose WebSocket client\n this.disposeWSClient();\n }\n\n /**************************************************************************\n * IS-A Child Entity Discovery\n *\n * Discovers which IS-A child entity, if any, has a record with the given\n * primary key. Calls the server-side FindISAChildEntity GraphQL query\n * which executes a single UNION ALL for efficiency.\n **************************************************************************/\n\n /**\n * Discovers which IS-A child entity has a record matching the given PK.\n * Calls the server-side FindISAChildEntity resolver via GraphQL.\n *\n * @param entityInfo The parent entity to check children for\n * @param recordPKValue The primary key value to search for in child tables\n * @param contextUser Optional context user (unused on client, present for interface parity)\n * @returns The child entity name if found, or null if no child record exists\n */\n public async FindISAChildEntity(\n entityInfo: EntityInfo,\n recordPKValue: string,\n contextUser?: UserInfo\n ): Promise<{ ChildEntityName: string } | null> {\n if (!entityInfo.IsParentType) return null;\n\n const gql = `query FindISAChildEntity($EntityName: String!, $RecordID: String!) {\n FindISAChildEntity(EntityName: $EntityName, RecordID: $RecordID) {\n Success\n ChildEntityName\n ErrorMessage\n }\n }`;\n\n try {\n const result = await this.ExecuteGQL(gql, {\n EntityName: entityInfo.Name,\n RecordID: recordPKValue\n });\n\n if (result?.FindISAChildEntity?.Success && result.FindISAChildEntity.ChildEntityName) {\n return { ChildEntityName: result.FindISAChildEntity.ChildEntityName };\n }\n return null;\n }\n catch (e) {\n LogError(`FindISAChildEntity failed for ${entityInfo.Name}: ${e}`);\n return null;\n }\n }\n\n /**\n * Discovers ALL IS-A child entities that have records matching the given PK.\n * Used for overlapping subtype parents (AllowMultipleSubtypes = true).\n * Calls the server-side FindISAChildEntities resolver via GraphQL.\n *\n * @param entityInfo The parent entity to check children for\n * @param recordPKValue The primary key value to search for in child tables\n * @param contextUser Optional context user (unused on client, present for interface parity)\n * @returns Array of child entity names found (empty if none)\n */\n public async FindISAChildEntities(\n entityInfo: EntityInfo,\n recordPKValue: string,\n contextUser?: UserInfo\n ): Promise<{ ChildEntityName: string }[]> {\n if (!entityInfo.IsParentType) return [];\n\n const gql = `query FindISAChildEntities($EntityName: String!, $RecordID: String!) {\n FindISAChildEntities(EntityName: $EntityName, RecordID: $RecordID) {\n Success\n ChildEntityNames\n ErrorMessage\n }\n }`;\n\n try {\n const result = await this.ExecuteGQL(gql, {\n EntityName: entityInfo.Name,\n RecordID: recordPKValue\n });\n\n if (result?.FindISAChildEntities?.Success && result.FindISAChildEntities.ChildEntityNames) {\n return result.FindISAChildEntities.ChildEntityNames.map(\n (name: string) => ({ ChildEntityName: name })\n );\n }\n return [];\n }\n catch (e) {\n LogError(`FindISAChildEntities failed for ${entityInfo.Name}: ${e}`);\n return [];\n }\n }\n}\n\n","import { RunReport, BaseEntity, Metadata, RunView, RunQuery, SetProvider, StartupManager } from \"@memberjunction/core\";\nimport { GraphQLDataProvider, GraphQLProviderConfigData } from \"./graphQLDataProvider\";\nimport { MJGlobal, MJEventType } from \"@memberjunction/global\";\n\n/**\n * Setup the GraphQL client for the project using the provided configuration data.\n */\nexport async function setupGraphQLClient(config: GraphQLProviderConfigData): Promise<GraphQLDataProvider> {\n // Set the provider for all entities to be GraphQL in this project, can use a different provider in other situations....\n const provider = new GraphQLDataProvider()\n\n // BaseEntity + Metadata share the same GraphQLDataProvider instance\n SetProvider(provider);\n\n await provider.Config(config);\n\n // fire off the logged in event if we get here\n await StartupManager.Instance.Startup();\n \n MJGlobal.Instance.RaiseEvent({ event: MJEventType.LoggedIn, eventCode: null, component: this, args: null });\n\n return provider;\n}","import { CompositeKey } from \"@memberjunction/core\";\n\n\n\nexport class SyncRolesAndUsersResult {\n Success: boolean;\n}\n \nexport class RoleInput {\n ID: string;\n\n Name: string;\n \n Description: string;\n}\n\n\nexport class UserInput {\n ID!: string;\n\n Name!: string;\n\n Email!: string;\n\n Type!: 'Owner' | 'User';\n\n FirstName: string;\n\n LastName: string;\n \n Title: string;\n\n Roles?: RoleInput[];\n}\n\nexport class RolesAndUsersInput {\n public Users: UserInput[];\n \n public Roles: RoleInput[];\n}\n\n\n\n/**\n * This type defines the possible list of actions that can be taken in syncing data: Create, Update, CreateOrUpdate, Delete, or DeleteWithFilter\n * DeleteWithFilter is where you specify a valid SQL expression that can be used in a where clause to get a list of records in a given entity to delete\n * this can be used to ensure cleaning out data from a subset of a given table.\n */\nexport enum SyncDataAction {\n Create = \"Create\",\n Update = \"Update\",\n CreateOrUpdate = \"CreateOrUpdate\",\n Delete = \"Delete\",\n DeleteWithFilter = \"DeleteWithFilter\"\n}\n \nexport class ActionItemInput {\n /**\n * The name of the entity where action is to be taken\n */\n EntityName!: string;\n /**\n * For Update, CreateOrUpdate and Delete operations either a PrimaryKey or an AlternateKey must be provided. For Create and DeleteWithFilter operations, neither is used. \n */\n PrimaryKey?: CompositeKey;\n /**\n * For Update, CreateOrUpdate and Delete operations either a PrimaryKey or an AlternateKey must be provided. For Create and DeleteWithFilter operations, neither is used. \n */\n AlternateKey?: CompositeKey;\n /**\n * The type of action requested. The possible values are Create, Update, CreateOrUpdate, Delete, DeleteWithFilter\n */\n Type!: SyncDataAction;\n /**\n * This field is a JSON representation of the field values of the entity to be created or updated. It is used for all ActionTypes except for \n */\n RecordJSON?: string;\n\n /**\n * This field is only provided when the Action Type is DeleteWithFilter. It is a valid SQL expression that can be used in a where clause to get a list of records in a given entity to delete\n */\n DeleteFilter?: string;\n}\n \n\nexport class SyncDataResult {\n Success: boolean;\n \n Results: ActionItemOutput[] = [];\n}\n\nexport class ActionItemOutput {\n Success: boolean;\n ErrorMessage: string;\n EntityName!: string;\n PrimaryKey?: CompositeKey;\n AlternateKey?: CompositeKey;\n Type!: SyncDataAction;\n\n /**\n * This field is a JSON representation of the field values of the entity to be created or updated. It is used for all ActionTypes except for \n */\n RecordJSON?: string;\n\n /**\n * This field is only provided when the Action Type is DeleteWithFilter. It is a valid SQL expression that can be used in a where clause to get a list of records in a given entity to delete\n */\n DeleteFilter?: string;\n}","import { CompositeKey, LogError, KeyValuePair, IsVerboseLoggingEnabled } from '@memberjunction/core';\nimport { SafeJSONParse } from '@memberjunction/global';\nimport { gql, GraphQLClient } from 'graphql-request'\nimport { ActionItemInput, RolesAndUsersInput, SyncDataResult, SyncRolesAndUsersResult } from './rolesAndUsersType';\nimport { \n RunAIPromptParams, \n RunAIPromptResult, \n ExecuteSimplePromptParams,\n SimplePromptResult,\n EmbedTextParams,\n EmbedTextResult\n} from './graphQLAIClient';\nimport { ExecuteAgentParams, ExecuteAgentResult } from '@memberjunction/ai-core-plus';\n\n/**\n * Specialized client that is designed to be used exclusively on the server side\n * by the System User using the MJ API KEY. This is designed to allow server side\n * code such as within the context of an MJAPI server to talk to another MAJPI server\n * but as the client.\n * \n * It is possible for a server to use the regular @GraphQLDataProvider client to talk\n * to another MJAPI server, but that would require the server to have a user account\n * and the server would have to be able to log in as that user via a JWT token. This\n * is not always possible or convenient in the flow. \n * \n * Also the standard client configuration process has a certain amount over overhead\n * in always loading up certain metadata that is not necessary for many system user\n * operations.\n * \n * Finaly, this client can be used in parallel with the regular client to allow a server\n * to mix and match the two as needed.\n */\nexport class GraphQLSystemUserClient {\n private _client: GraphQLClient;\n private _sessionId: string;\n /**\n * Returns the underlying GraphQL client which is an instance of the GraphQLClient object\n * from the graphql-request package. This is useful if you want to perform any operation\n * that is not directly supported by this class via specialized methods.\n */\n public get Client(): GraphQLClient {\n return this._client;\n }\n\n /**\n * @param url MJAPI server URL\n * @param token Optional, JWT token that is used for a normal user authentication flow. This is required if mjAPIKey is not provided.\n * @param sessionId Optional, UUID that is used to track a session. This can be any string.\n * @param mjAPIKey Shared Secret key that is provided for system user authentication. This is required if token is not provided.\n * @returns \n */\n constructor (url: string, token: string, sessionId: string, mjAPIKey: string) {\n const headers: Record<string, string> = { \n 'x-session-id': sessionId,\n };\n this._sessionId = sessionId;\n if (token)\n headers.authorization = 'Bearer ' + token;\n if (mjAPIKey)\n headers['x-mj-api-key'] = mjAPIKey;\n\n this._client = new GraphQLClient(url, {\n headers\n });\n }\n\n /**\n * Calls the GetData() query on the server to execute any number of provided SQL queries in parallel and returns the results.\n * The queries MUST BE read only and not perform any DML operations. The remote server will execute the queries using a special\n * lower-privilege user that is not allowed to perform any form of write operations.\n * @param queries an array of SQL queries to execute on the remote server\n * @param accessToken the short-lived access token that is required to perform this operation. This is different from the MJAPI key and is used for a second factor and is a short-lived token. You will receive this token \n * when an MJAPI calls your server to request something along with the URL to call back.\n * @returns \n */\n public async GetData(queries: string[], accessToken: string): Promise<GetDataOutput> {\n try {\n const query = `query GetData($input: GetDataInputType!) {\n GetData(input: $input) {\n Success\n ErrorMessages\n Queries\n Results\n }\n }`\n const result = await this.Client.request(query, {input: {Queries: queries, Token: accessToken}}) as {GetData: GetDataOutput};\n if (result && result.GetData) {\n // for each succesful item, we will parse and return the array of objects instead of the string\n return {\n Success: result.GetData.Success,\n Results: result.GetData.Results.map(r => r ? SafeJSONParse(r) : null),\n ErrorMessages: result.GetData.ErrorMessages,\n Queries: result.GetData.Queries \n }\n }\n else {\n return {\n Success: false,\n Results: [],\n ErrorMessages: result.GetData?.ErrorMessages ?? ['Unknown error'],\n Queries: result.GetData?.Queries ?? queries\n }\n }\n }\n catch (e) {\n // Extract clean error message - the GraphQL error response contains the actual SQL error\n let cleanError = e instanceof Error ? e.message : String(e);\n\n // Try to extract just the SQL error from GraphQL response\n // Look for the actual error message before the JSON payload\n const match = cleanError.match(/Error: ([^:]+)\\./);\n if (match) {\n cleanError = match[1] + '.';\n }\n\n // Only log verbose details if in verbose mode\n if (IsVerboseLoggingEnabled()) {\n const verboseError = `GraphQLSystemUserClient::GetData - Error getting data - ${e}`;\n LogError(verboseError);\n }\n\n return {\n Success: false,\n Results: [],\n ErrorMessages: [cleanError],\n Queries: queries\n }\n }\n }\n\n /**\n * This method will return a list of all entities that are available on the remote server. This is a lightweight call that only returns the basic metadata for each entity and does not include all of the attributes at \n * either the entity or the field level. This is useful for getting a list of entities that are available on the remote server and knowing their IDs and Entity Field IDs on the remote server. For core MemberJunction \n * entities and entity fields, the ID values are globally unique and set by the MemberJunction distribution, however, for other entities that are generated on each target system they can vary so it is best to use\n * lookups name or a combination of SchemaName and BaseTable to uniquely identify an entity.\n * @param client \n * @returns \n */\n public async GetAllRemoteEntities(): Promise<SimpleRemoteEntityOutput> {\n try {\n const query = `query GetAllEntities {\n GetAllEntities {\n Success\n ErrorMessage\n Results {\n ID\n Name\n Description\n SchemaName\n BaseView\n BaseTable\n CodeName\n ClassName\n Fields {\n ID\n Name\n Description\n Type\n AllowsNull\n MaxLength\n }\n }\n }\n }`\n\n const result = (await this.Client.request(query)) as {GetAllEntities: SimpleRemoteEntityOutput};\n if (result && result.GetAllEntities) {\n return result.GetAllEntities;\n }\n else {\n return {\n Success: false,\n Results: [],\n ErrorMessage: result.GetAllEntities?.ErrorMessage ?? 'Unknown error'\n }\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::GetAllRemoteEntities - Error getting remote entities - ${e}`);\n return {\n Success: false,\n Results: [],\n ErrorMessage: e\n }\n }\n }\n\n /**\n * This method is used to invoke the data synchronization mutation on the remote server. This method is used to create, update, or delete records in the remote server. The method takes an array of ActionItemInput objects\n * Each of the ActionItemInput objects represents a single action to take on a single entity. The action can be Create, Update, CreateOrUpdate, Delete, or DeleteWithFilter. The RecordJSON field is used for Create, CreateOrUpdate and Update whereas\n * the DeleteFilter field is used for DeleteWithFilter. The PrimaryKey and AlternateKey fields are used to identify the record to be acted upon. \n * \n * Use of the AlternateKey is important for situations where you might have divergent primary keys across systems. For example for user entities that are individually generated on each system by CodeGen, the primary key will\n * be different per system, so you would use the combination of the SchemaName and BaseTable to identify the entity and then use the AlternateKey to provide the combination of these fields to uniquely identify the record for updates.\n * @param items \n * @returns \n */\n public async SyncData(items: ActionItemInput[]): Promise<SyncDataResult> {\n try {\n // call the resolver to sync the roles and users\n const query = `mutation SyncData($items: [ActionItemInputType!]!) {\n SyncData(items: $items) {\n Success\n Results {\n Success\n ErrorMessage\n EntityName\n Type\n PrimaryKey {\n KeyValuePairs {\n FieldName\n Value \n }\n }\n AlternateKey {\n KeyValuePairs {\n FieldName\n Value \n }\n }\n DeleteFilter\n RecordJSON\n }\n }\n }`\n const d = <{SyncData: SyncDataResult}>await this.Client.request(query, {items});\n if (d && d.SyncData) {\n return d.SyncData;\n }\n else {\n return {\n Success: false,\n Results: []\n }\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::SyncData - Error syncing data - ${e}`);\n return {\n Success: false,\n Results: []\n }\n }\n }\n\n /**\n * This method will sync the roles and users on the remote server. This method is used to create, update, or delete roles and users on the remote server. \n * The method takes a RolesAndUsersInput object that contains an array of RoleInput objects. Note that this will not result in the removal of roles on the \n * remote server that are deemed to be built-in MemberJunction roles such as Developer, Integration and UI.\n * @param data \n * @returns \n */\n public async SyncRolesAndUsers(data: RolesAndUsersInput): Promise<SyncRolesAndUsersResult> {\n try {\n // call the resolver to sync the roles and users\n const query = `mutation SyncRolesAndUsers($data: RolesAndUsersInputType!) {\n SyncRolesAndUsers(data: $data) {\n Success\n }\n }`\n const d = await this.Client.request(query, {data}) as {SyncRolesAndUsers: SyncRolesAndUsersResult};\n if (d && d.SyncRolesAndUsers) {\n return d.SyncRolesAndUsers;\n }\n else {\n return {\n Success: false\n }\n }\n } \n catch (e) {\n LogError(`GraphQLSystemUserClient::SyncRolesAndUsers - Error syncing roles and users - ${e}`);\n return {\n Success: false\n }\n } \n }\n\n /**\n * Runs a view by name using the RunViewByNameSystemUser resolver.\n * @param input - View input parameters for running by name\n * @returns Promise containing the view execution results\n */\n public async RunViewByName(input: RunViewByNameSystemUserInput): Promise<RunViewSystemUserResult> {\n try {\n const query = `query RunViewByNameSystemUser($input: RunViewByNameInput!) {\n RunViewByNameSystemUser(input: $input) {\n Results {\n PrimaryKey {\n FieldName\n Value\n }\n EntityID\n Data\n }\n UserViewRunID\n RowCount\n TotalRowCount\n ExecutionTime\n ErrorMessage\n Success\n }\n }`\n\n const result = await this.Client.request(query, { input }) as { RunViewByNameSystemUser: RunViewSystemUserResult };\n if (result && result.RunViewByNameSystemUser) {\n return result.RunViewByNameSystemUser;\n } else {\n return {\n Results: [],\n Success: false,\n ErrorMessage: 'Failed to execute view by name'\n };\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::RunViewByNameSystemUser - Error running view by name - ${e}`);\n return {\n Results: [],\n Success: false,\n ErrorMessage: e.toString()\n };\n }\n }\n\n /**\n * Runs a view by ID using the RunViewByIDSystemUser resolver.\n * @param input - View input parameters for running by ID\n * @returns Promise containing the view execution results\n */\n public async RunViewByID(input: RunViewByIDSystemUserInput): Promise<RunViewSystemUserResult> {\n try {\n const query = `query RunViewByIDSystemUser($input: RunViewByIDInput!) {\n RunViewByIDSystemUser(input: $input) {\n Results {\n PrimaryKey {\n FieldName\n Value\n }\n EntityID\n Data\n }\n UserViewRunID\n RowCount\n TotalRowCount\n ExecutionTime\n ErrorMessage\n Success\n }\n }`\n\n const result = await this.Client.request(query, { input }) as { RunViewByIDSystemUser: RunViewSystemUserResult };\n if (result && result.RunViewByIDSystemUser) {\n return result.RunViewByIDSystemUser;\n } else {\n return {\n Results: [],\n Success: false,\n ErrorMessage: 'Failed to execute view by ID'\n };\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::RunViewByIDSystemUser - Error running view by ID - ${e}`);\n return {\n Results: [],\n Success: false,\n ErrorMessage: e.toString()\n };\n }\n }\n\n /**\n * Runs a dynamic view using the RunDynamicViewSystemUser resolver.\n * @param input - View input parameters for dynamic view execution\n * @returns Promise containing the view execution results\n */\n public async RunDynamicView(input: RunDynamicViewSystemUserInput): Promise<RunViewSystemUserResult> {\n try {\n const query = `query RunDynamicViewSystemUser($input: RunDynamicViewInput!) {\n RunDynamicViewSystemUser(input: $input) {\n Results {\n PrimaryKey {\n FieldName\n Value\n }\n EntityID\n Data\n }\n UserViewRunID\n RowCount\n TotalRowCount\n ExecutionTime\n ErrorMessage\n Success\n }\n }`\n\n const result = await this.Client.request(query, { input }) as { RunDynamicViewSystemUser: RunViewSystemUserResult };\n if (result && result.RunDynamicViewSystemUser) {\n return result.RunDynamicViewSystemUser;\n } else {\n return {\n Results: [],\n Success: false,\n ErrorMessage: 'Failed to execute dynamic view'\n };\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::RunDynamicViewSystemUser - Error running dynamic view - ${e}`);\n return {\n Results: [],\n Success: false,\n ErrorMessage: e.toString()\n };\n }\n }\n\n /**\n * Runs multiple views using the RunViewsSystemUser resolver. This method allows system users\n * to execute view queries with the same functionality as regular users but with system-level privileges.\n * @param input - Array of view input parameters\n * @returns Promise containing the results from all view executions\n */\n public async RunViews(input: RunViewSystemUserInput[]): Promise<RunViewSystemUserResult[]> {\n try {\n const query = `query RunViewsSystemUser($input: [RunViewGenericInput!]!) {\n RunViewsSystemUser(input: $input) {\n Results {\n PrimaryKey {\n FieldName\n Value\n }\n EntityID\n Data\n }\n UserViewRunID\n RowCount\n TotalRowCount\n ExecutionTime\n ErrorMessage\n Success\n }\n }`\n\n const result = await this.Client.request(query, { input }) as { RunViewsSystemUser: RunViewSystemUserResult[] };\n if (result && result.RunViewsSystemUser) {\n return result.RunViewsSystemUser;\n } else {\n return [];\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::RunViewsSystemUser - Error running views - ${e}`);\n return [];\n }\n }\n\n /**\n * Executes a stored query by ID using the GetQueryDataSystemUser resolver.\n * @param input - Query input parameters for execution by ID\n * @returns Promise containing the query execution results\n */\n public async GetQueryData(input: GetQueryDataSystemUserInput): Promise<RunQuerySystemUserResult> {\n try {\n // Validate that Parameters is a JSON object, not an array\n if (input.Parameters !== undefined && Array.isArray(input.Parameters)) {\n throw new Error('Parameters must be a JSON object, not an array. Use {} for empty parameters instead of [].');\n }\n\n const query = `query GetQueryDataSystemUser($QueryID: String!, $CategoryID: String, $CategoryPath: String, $Parameters: JSONObject, $MaxRows: Int, $StartRow: Int) {\n GetQueryDataSystemUser(QueryID: $QueryID, CategoryID: $CategoryID, CategoryPath: $CategoryPath, Parameters: $Parameters, MaxRows: $MaxRows, StartRow: $StartRow) {\n QueryID\n QueryName\n Success\n Results\n RowCount\n TotalRowCount\n ExecutionTime\n ErrorMessage\n AppliedParameters\n }\n }`\n\n const variables: any = { QueryID: input.QueryID };\n if (input.CategoryID !== undefined) variables.CategoryID = input.CategoryID;\n if (input.CategoryPath !== undefined) variables.CategoryPath = input.CategoryPath;\n if (input.Parameters !== undefined) variables.Parameters = input.Parameters;\n if (input.MaxRows !== undefined) variables.MaxRows = input.MaxRows;\n if (input.StartRow !== undefined) variables.StartRow = input.StartRow;\n\n const result = await this.Client.request(query, variables) as { GetQueryDataSystemUser: RunQuerySystemUserResult };\n \n if (result && result.GetQueryDataSystemUser) {\n // Parse the JSON results for easier consumption\n return {\n ...result.GetQueryDataSystemUser,\n Results: result.GetQueryDataSystemUser.Results ? SafeJSONParse(result.GetQueryDataSystemUser.Results) : null\n };\n } else {\n return {\n QueryID: input.QueryID,\n QueryName: '',\n Success: false,\n Results: null,\n RowCount: 0,\n TotalRowCount: 0,\n ExecutionTime: 0,\n ErrorMessage: 'Query execution failed'\n };\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::GetQueryDataSystemUser - Error executing query - ${e}`);\n return {\n QueryID: input.QueryID,\n QueryName: '',\n Success: false,\n Results: null,\n RowCount: 0,\n TotalRowCount: 0,\n ExecutionTime: 0,\n ErrorMessage: e.toString()\n };\n }\n }\n\n /**\n * Executes a stored query by name using the GetQueryDataByNameSystemUser resolver.\n * @param input - Query input parameters for execution by name\n * @returns Promise containing the query execution results\n */\n public async GetQueryDataByName(input: GetQueryDataByNameSystemUserInput): Promise<RunQuerySystemUserResult> {\n try {\n // Validate that Parameters is a JSON object, not an array\n if (input.Parameters !== undefined && Array.isArray(input.Parameters)) {\n throw new Error('Parameters must be a JSON object, not an array. Use {} for empty parameters instead of [].');\n }\n\n const query = `query GetQueryDataByNameSystemUser($QueryName: String!, $CategoryID: String, $CategoryPath: String, $Parameters: JSONObject, $MaxRows: Int, $StartRow: Int) {\n GetQueryDataByNameSystemUser(QueryName: $QueryName, CategoryID: $CategoryID, CategoryPath: $CategoryPath, Parameters: $Parameters, MaxRows: $MaxRows, StartRow: $StartRow) {\n QueryID\n QueryName\n Success\n Results\n RowCount\n TotalRowCount\n ExecutionTime\n ErrorMessage\n AppliedParameters\n }\n }`\n\n const variables: any = { QueryName: input.QueryName };\n if (input.CategoryID !== undefined) variables.CategoryID = input.CategoryID;\n if (input.CategoryPath !== undefined) variables.CategoryPath = input.CategoryPath;\n if (input.Parameters !== undefined) variables.Parameters = input.Parameters;\n if (input.MaxRows !== undefined) variables.MaxRows = input.MaxRows;\n if (input.StartRow !== undefined) variables.StartRow = input.StartRow;\n\n const result = await this.Client.request(query, variables) as { GetQueryDataByNameSystemUser: RunQuerySystemUserResult };\n \n if (result && result.GetQueryDataByNameSystemUser) {\n // Parse the JSON results for easier consumption\n return {\n ...result.GetQueryDataByNameSystemUser,\n Results: result.GetQueryDataByNameSystemUser.Results ? SafeJSONParse(result.GetQueryDataByNameSystemUser.Results) : null\n };\n } else {\n return {\n QueryID: '',\n QueryName: input.QueryName,\n Success: false,\n Results: null,\n RowCount: 0,\n TotalRowCount: 0,\n ExecutionTime: 0,\n ErrorMessage: 'Query execution failed'\n };\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::GetQueryDataByNameSystemUser - Error executing query - ${e}`);\n return {\n QueryID: '',\n QueryName: input.QueryName,\n Success: false,\n Results: null,\n RowCount: 0,\n TotalRowCount: 0,\n ExecutionTime: 0,\n ErrorMessage: e.toString()\n };\n }\n }\n\n /**\n * Creates a new query using the CreateQuerySystemUser mutation. This method is restricted to system users only.\n * @param input - CreateQuerySystemUserInput containing all the query attributes including optional CategoryPath\n * @returns Promise containing the result of the query creation\n */\n public async CreateQuery(input: CreateQueryInput): Promise<CreateQueryResult> {\n try {\n const query = `mutation CreateQuerySystemUser($input: CreateQuerySystemUserInput!) {\n CreateQuerySystemUser(input: $input) {\n Success\n ErrorMessage\n ID\n Name\n Description\n CategoryID\n Category\n SQL\n Status\n QualityRank\n EmbeddingVector\n EmbeddingModelID\n EmbeddingModelName\n Fields {\n ID\n QueryID\n Name\n Description\n Sequence\n SQLBaseType\n SQLFullType\n SourceEntityID\n SourceEntity\n SourceFieldName\n IsComputed\n ComputationDescription\n IsSummary\n SummaryDescription\n }\n Parameters {\n ID\n QueryID\n Name\n Description\n Type\n IsRequired\n DefaultValue\n SampleValue\n ValidationFilters\n }\n Entities {\n ID\n QueryID\n EntityID\n Entity\n }\n Permissions {\n ID\n QueryID\n RoleID\n Role\n }\n }\n }`\n\n const result = await this.Client.request(query, { input }) as { CreateQuerySystemUser: CreateQueryResult };\n if (result && result.CreateQuerySystemUser) {\n return result.CreateQuerySystemUser;\n } else {\n return {\n Success: false,\n ErrorMessage: 'Failed to create query'\n };\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::CreateQuery - Error creating query - ${e}`);\n return {\n Success: false,\n ErrorMessage: e.toString()\n };\n }\n }\n\n /**\n * Updates an existing query with the provided attributes. This method is restricted to system users only.\n * @param input - UpdateQueryInput containing the query ID and fields to update\n * @returns Promise containing the result of the query update including updated fields, parameters, entities, and permissions\n */\n public async UpdateQuery(input: UpdateQueryInput): Promise<UpdateQueryResult> {\n try {\n const query = `mutation UpdateQuerySystemUser($input: UpdateQuerySystemUserInput!) {\n UpdateQuerySystemUser(input: $input) {\n Success\n ErrorMessage\n ID\n Name\n Description\n CategoryID\n Category\n SQL\n Status\n QualityRank\n EmbeddingVector\n EmbeddingModelID\n EmbeddingModelName\n Fields {\n ID\n QueryID\n Name\n Description\n Sequence\n SQLBaseType\n SQLFullType\n SourceEntityID\n SourceEntity\n SourceFieldName\n IsComputed\n ComputationDescription\n IsSummary\n SummaryDescription\n }\n Parameters {\n ID\n QueryID\n Name\n Description\n Type\n IsRequired\n DefaultValue\n SampleValue\n ValidationFilters\n }\n Entities {\n ID\n QueryID\n EntityID\n Entity\n }\n Permissions {\n ID\n QueryID\n RoleID\n Role\n }\n }\n }`\n\n const result = await this.Client.request(query, { input }) as { UpdateQuerySystemUser: UpdateQueryResult };\n if (result && result.UpdateQuerySystemUser) {\n return result.UpdateQuerySystemUser;\n } else {\n return {\n Success: false,\n ErrorMessage: 'Failed to update query'\n };\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::UpdateQuery - Error updating query - ${e}`);\n return {\n Success: false,\n ErrorMessage: e.toString()\n };\n }\n }\n\n /**\n * Deletes a query by ID using the DeleteQuerySystemResolver mutation. This method is restricted to system users only.\n * @param ID - The ID of the query to delete\n * @param options - Optional delete options controlling action execution\n * @returns Promise containing the result of the query deletion\n */\n public async DeleteQuery(ID: string, options?: DeleteQueryOptionsInput): Promise<DeleteQueryResult> {\n try {\n // Validate ID is not null/undefined/empty\n if (!ID || ID.trim() === '') {\n LogError('GraphQLSystemUserClient::DeleteQuery - Invalid query ID: ID cannot be null or empty');\n return {\n Success: false,\n ErrorMessage: 'Invalid query ID: ID cannot be null or empty'\n };\n }\n\n const query = `mutation DeleteQuerySystemResolver($ID: String!, $options: DeleteOptionsInput) {\n DeleteQuerySystemResolver(ID: $ID, options: $options) {\n Success\n ErrorMessage\n ID\n Name\n }\n }`\n\n const variables: Record<string, unknown> = { ID: ID };\n if (options !== undefined) {\n // Apply defaults for all required fields in DeleteOptionsInput\n // The server requires all fields to be present\n variables.options = {\n SkipEntityAIActions: options.SkipEntityAIActions ?? false,\n SkipEntityActions: options.SkipEntityActions ?? false,\n ReplayOnly: options.ReplayOnly ?? false,\n IsParentEntityDelete: options.IsParentEntityDelete ?? false\n };\n }\n\n const result = await this.Client.request(query, variables) as { DeleteQuerySystemResolver: DeleteQueryResult };\n \n if (result && result.DeleteQuerySystemResolver) {\n return result.DeleteQuerySystemResolver;\n } else {\n return {\n Success: false,\n ErrorMessage: 'Failed to delete query'\n };\n }\n }\n catch (e: unknown) {\n // Extract detailed error information for debugging\n let errorDetails = '';\n if (e instanceof Error) {\n errorDetails = e.message;\n // Check for cause (common in fetch errors)\n if ('cause' in e && e.cause) {\n const cause = e.cause as Error;\n errorDetails += ` | Cause: ${cause.message || cause}`;\n if ('code' in cause) {\n errorDetails += ` | Code: ${(cause as NodeJS.ErrnoException).code}`;\n }\n }\n // Check for response details (GraphQL client errors)\n if ('response' in e) {\n const response = (e as { response?: { status?: number; errors?: unknown[] } }).response;\n if (response?.status) {\n errorDetails += ` | HTTP Status: ${response.status}`;\n }\n if (response?.errors) {\n errorDetails += ` | GraphQL Errors: ${JSON.stringify(response.errors)}`;\n }\n }\n // Include stack trace for debugging\n if (e.stack) {\n console.error('DeleteQuery stack trace:', e.stack);\n }\n } else {\n errorDetails = String(e);\n }\n\n LogError(`GraphQLSystemUserClient::DeleteQuery - Error deleting query - ${errorDetails}`);\n return {\n Success: false,\n ErrorMessage: errorDetails\n };\n }\n }\n\n /**\n * Run an AI prompt with system user privileges.\n * This method allows system-level execution of AI prompts without user authentication.\n * \n * @param params The parameters for running the AI prompt\n * @returns A Promise that resolves to a RunAIPromptResult object\n * \n * @example\n * ```typescript\n * const result = await systemClient.RunAIPrompt({\n * promptId: \"prompt-id\",\n * data: { systemData: \"value\" },\n * skipValidation: true\n * });\n * ```\n */\n public async RunAIPrompt(params: RunAIPromptParams): Promise<RunAIPromptResult> {\n try {\n // Build the query for system user\n const query = gql`\n query RunAIPromptSystemUser(\n $promptId: String!,\n $data: String,\n $overrideModelId: String,\n $overrideVendorId: String,\n $configurationId: String,\n $skipValidation: Boolean,\n $templateData: String,\n $responseFormat: String,\n $temperature: Float,\n $topP: Float,\n $topK: Int,\n $minP: Float,\n $frequencyPenalty: Float,\n $presencePenalty: Float,\n $seed: Int,\n $stopSequences: [String!],\n $includeLogProbs: Boolean,\n $topLogProbs: Int,\n $messages: String,\n $rerunFromPromptRunID: String,\n $systemPromptOverride: String\n ) {\n RunAIPromptSystemUser(\n promptId: $promptId,\n data: $data,\n overrideModelId: $overrideModelId,\n overrideVendorId: $overrideVendorId,\n configurationId: $configurationId,\n skipValidation: $skipValidation,\n templateData: $templateData,\n responseFormat: $responseFormat,\n temperature: $temperature,\n topP: $topP,\n topK: $topK,\n minP: $minP,\n frequencyPenalty: $frequencyPenalty,\n presencePenalty: $presencePenalty,\n seed: $seed,\n stopSequences: $stopSequences,\n includeLogProbs: $includeLogProbs,\n topLogProbs: $topLogProbs,\n messages: $messages,\n rerunFromPromptRunID: $rerunFromPromptRunID,\n systemPromptOverride: $systemPromptOverride\n ) {\n success\n output\n parsedResult\n error\n executionTimeMs\n tokensUsed\n promptRunId\n rawResult\n validationResult\n chatResult\n }\n }\n `;\n\n // Prepare variables\n const variables = this.preparePromptVariables(params);\n\n // Execute the query\n const result = await this.Client.request(query, variables) as { RunAIPromptSystemUser: any };\n\n // Process and return the result\n if (result && result.RunAIPromptSystemUser) {\n return this.processPromptResult(result.RunAIPromptSystemUser);\n } else {\n return {\n success: false,\n error: 'Failed to execute AI prompt as system user'\n };\n }\n } catch (e) {\n LogError(`GraphQLSystemUserClient::RunAIPrompt - Error running AI prompt - ${e}`);\n return {\n success: false,\n error: e.toString()\n };\n }\n }\n\n /**\n * Run an AI agent with system user privileges.\n * This method allows system-level execution of AI agents without user authentication.\n * \n * @param params The parameters for running the AI agent\n * @returns A Promise that resolves to a RunAIAgentResult object\n * \n * @example\n * ```typescript\n * const result = await systemClient.RunAIAgent({\n * agentId: \"agent-id\",\n * messages: [{ role: \"system\", content: \"Process data\" }],\n * sessionId: \"system-session\"\n * });\n * ```\n */\n public async RunAIAgent(params: ExecuteAgentParams): Promise<ExecuteAgentResult> {\n try {\n // Build the query for system user\n const query = gql`\n query RunAIAgentSystemUser(\n $agentId: String!,\n $messages: String!,\n $sessionId: String!,\n $data: String,\n $templateData: String,\n $lastRunId: String,\n $autoPopulateLastRunPayload: Boolean,\n $configurationId: String\n ) {\n RunAIAgentSystemUser(\n agentId: $agentId,\n messages: $messages,\n sessionId: $sessionId,\n data: $data,\n templateData: $templateData,\n lastRunId: $lastRunId,\n autoPopulateLastRunPayload: $autoPopulateLastRunPayload,\n configurationId: $configurationId\n ) {\n success\n errorMessage\n executionTimeMs\n result\n }\n }\n `;\n\n // Prepare variables\n const variables = this.prepareAgentVariables(params);\n\n // Execute the query\n const result = await this.Client.request(query, variables) as { RunAIAgentSystemUser: any };\n\n // Process and return the result\n if (result && result.RunAIAgentSystemUser) {\n return this.processAgentResult(result.RunAIAgentSystemUser.result);\n } else {\n return {\n success: false, \n agentRun: undefined\n };\n }\n } catch (e) {\n LogError(`GraphQLSystemUserClient::RunAIAgent - Error running AI agent - ${e}`);\n return {\n success: false, \n agentRun: undefined\n };\n }\n }\n\n /**\n * Helper method to prepare prompt variables for GraphQL\n * @private\n */\n private preparePromptVariables(params: RunAIPromptParams): Record<string, any> {\n const variables: Record<string, any> = {\n promptId: params.promptId\n };\n\n // Serialize complex objects to JSON strings\n if (params.data !== undefined) {\n variables.data = typeof params.data === 'object' ? JSON.stringify(params.data) : params.data;\n }\n if (params.templateData !== undefined) {\n variables.templateData = typeof params.templateData === 'object' ? JSON.stringify(params.templateData) : params.templateData;\n }\n if (params.messages !== undefined) {\n variables.messages = JSON.stringify(params.messages);\n }\n\n // Add optional scalar parameters\n if (params.overrideModelId !== undefined) variables.overrideModelId = params.overrideModelId;\n if (params.overrideVendorId !== undefined) variables.overrideVendorId = params.overrideVendorId;\n if (params.configurationId !== undefined) variables.configurationId = params.configurationId;\n if (params.skipValidation !== undefined) variables.skipValidation = params.skipValidation;\n if (params.responseFormat !== undefined) variables.responseFormat = params.responseFormat;\n if (params.temperature !== undefined) variables.temperature = params.temperature;\n if (params.topP !== undefined) variables.topP = params.topP;\n if (params.topK !== undefined) variables.topK = params.topK;\n if (params.minP !== undefined) variables.minP = params.minP;\n if (params.frequencyPenalty !== undefined) variables.frequencyPenalty = params.frequencyPenalty;\n if (params.presencePenalty !== undefined) variables.presencePenalty = params.presencePenalty;\n if (params.seed !== undefined) variables.seed = params.seed;\n if (params.stopSequences !== undefined) variables.stopSequences = params.stopSequences;\n if (params.includeLogProbs !== undefined) variables.includeLogProbs = params.includeLogProbs;\n if (params.topLogProbs !== undefined) variables.topLogProbs = params.topLogProbs;\n if (params.rerunFromPromptRunID !== undefined) variables.rerunFromPromptRunID = params.rerunFromPromptRunID;\n if (params.systemPromptOverride !== undefined) variables.systemPromptOverride = params.systemPromptOverride;\n\n return variables;\n }\n\n /**\n * Helper method to prepare agent variables for GraphQL\n * @private\n */\n private prepareAgentVariables(params: ExecuteAgentParams): Record<string, any> {\n const variables: Record<string, any> = {\n agentId: params.agent.ID,\n messages: JSON.stringify(params.conversationMessages),\n sessionId: this._sessionId\n };\n\n // Serialize optional complex objects to JSON strings\n if (params.data !== undefined) {\n variables.data = typeof params.data === 'object' ? JSON.stringify(params.data) : params.data;\n }\n\n // Add optional scalar parameters\n if (params.lastRunId !== undefined) variables.lastRunId = params.lastRunId;\n if (params.autoPopulateLastRunPayload !== undefined) variables.autoPopulateLastRunPayload = params.autoPopulateLastRunPayload;\n if (params.configurationId !== undefined) variables.configurationId = params.configurationId;\n\n return variables;\n }\n\n /**\n * Helper method to process prompt results\n * @private\n */\n private processPromptResult(promptResult: any): RunAIPromptResult {\n // Parse JSON results if they exist\n let parsedResult: any;\n let validationResult: any;\n let chatResult: any;\n\n try {\n if (promptResult.parsedResult) {\n parsedResult = JSON.parse(promptResult.parsedResult);\n }\n } catch (e) {\n parsedResult = promptResult.parsedResult;\n }\n\n try {\n if (promptResult.validationResult) {\n validationResult = JSON.parse(promptResult.validationResult);\n }\n } catch (e) {\n validationResult = promptResult.validationResult;\n }\n\n try {\n if (promptResult.chatResult) {\n chatResult = JSON.parse(promptResult.chatResult);\n }\n } catch (e) {\n chatResult = promptResult.chatResult;\n }\n\n return {\n success: promptResult.success,\n output: promptResult.output,\n parsedResult,\n error: promptResult.error,\n executionTimeMs: promptResult.executionTimeMs,\n tokensUsed: promptResult.tokensUsed,\n promptRunId: promptResult.promptRunId,\n rawResult: promptResult.rawResult,\n validationResult,\n chatResult\n };\n }\n\n /**\n * Helper method to process agent results\n * @private\n */\n private processAgentResult(agentResult: any): ExecuteAgentResult {\n return SafeJSONParse(agentResult) as ExecuteAgentResult;\n }\n\n /**\n * Execute a simple prompt without requiring a stored AI Prompt entity.\n * This method allows system-level execution of simple prompts.\n * \n * @param params The parameters for the simple prompt execution\n * @returns A Promise that resolves to a SimplePromptResult object\n * \n * @example\n * ```typescript\n * const result = await systemClient.ExecuteSimplePrompt({\n * systemPrompt: \"You are a data analyst\",\n * modelPower: \"medium\"\n * });\n * ```\n */\n public async ExecuteSimplePrompt(params: ExecuteSimplePromptParams): Promise<SimplePromptResult> {\n try {\n const query = gql`\n query ExecuteSimplePromptSystemUser(\n $systemPrompt: String!,\n $messages: String,\n $preferredModels: [String!],\n $modelPower: String,\n $responseFormat: String\n ) {\n ExecuteSimplePromptSystemUser(\n systemPrompt: $systemPrompt,\n messages: $messages,\n preferredModels: $preferredModels,\n modelPower: $modelPower,\n responseFormat: $responseFormat\n ) {\n success\n result\n resultObject\n modelName\n error\n executionTimeMs\n }\n }\n `;\n\n // Prepare variables\n const variables: Record<string, any> = {\n systemPrompt: params.systemPrompt\n };\n\n // Convert messages array to JSON string if provided\n if (params.messages && params.messages.length > 0) {\n variables.messages = JSON.stringify(params.messages);\n }\n\n if (params.preferredModels) {\n variables.preferredModels = params.preferredModels;\n }\n\n if (params.modelPower) {\n variables.modelPower = params.modelPower;\n }\n\n if (params.responseFormat) {\n variables.responseFormat = params.responseFormat;\n }\n\n // Execute the query\n const result = await this.Client.request(query, variables) as { ExecuteSimplePromptSystemUser: any };\n\n if (!result?.ExecuteSimplePromptSystemUser) {\n return {\n success: false,\n modelName: 'Unknown',\n error: 'Failed to execute simple prompt as system user'\n };\n }\n\n const promptResult = result.ExecuteSimplePromptSystemUser;\n\n // Parse resultObject if it exists\n let resultObject: any;\n if (promptResult.resultObject) {\n try {\n resultObject = JSON.parse(promptResult.resultObject);\n } catch (e) {\n resultObject = promptResult.resultObject;\n }\n }\n\n return {\n success: promptResult.success,\n result: promptResult.result,\n resultObject,\n modelName: promptResult.modelName,\n error: promptResult.error,\n executionTimeMs: promptResult.executionTimeMs\n };\n\n } catch (e) {\n LogError(`GraphQLSystemUserClient::ExecuteSimplePrompt - Error executing simple prompt - ${e}`);\n return {\n success: false,\n modelName: 'Unknown',\n error: e.toString()\n };\n }\n }\n\n /**\n * Generate embeddings using local embedding models.\n * This method allows system-level generation of text embeddings.\n * \n * @param params The parameters for embedding generation\n * @returns A Promise that resolves to an EmbedTextResult object\n * \n * @example\n * ```typescript\n * const result = await systemClient.EmbedText({\n * textToEmbed: [\"System data\", \"Configuration\"],\n * modelSize: \"small\"\n * });\n * ```\n */\n public async EmbedText(params: EmbedTextParams): Promise<EmbedTextResult> {\n try {\n const query = gql`\n query EmbedTextSystemUser(\n $textToEmbed: [String!]!,\n $modelSize: String!\n ) {\n EmbedTextSystemUser(\n textToEmbed: $textToEmbed,\n modelSize: $modelSize\n ) {\n embeddings\n modelName\n vectorDimensions\n error\n }\n }\n `;\n\n // Prepare variables - handle both single string and array\n const textArray = Array.isArray(params.textToEmbed) \n ? params.textToEmbed \n : [params.textToEmbed];\n\n const variables = {\n textToEmbed: textArray,\n modelSize: params.modelSize\n };\n\n // Execute the query\n const result = await this.Client.request(query, variables) as { EmbedTextSystemUser: any };\n\n if (!result?.EmbedTextSystemUser) {\n return {\n embeddings: Array.isArray(params.textToEmbed) ? [] : [],\n modelName: 'Unknown',\n vectorDimensions: 0,\n error: 'Failed to generate embeddings as system user'\n };\n }\n\n const embedResult = result.EmbedTextSystemUser;\n\n // Return single embedding or array based on input\n const returnEmbeddings = Array.isArray(params.textToEmbed)\n ? embedResult.embeddings\n : embedResult.embeddings[0];\n\n return {\n embeddings: returnEmbeddings,\n modelName: embedResult.modelName,\n vectorDimensions: embedResult.vectorDimensions,\n error: embedResult.error\n };\n\n } catch (e) {\n LogError(`GraphQLSystemUserClient::EmbedText - Error generating embeddings - ${e}`);\n return {\n embeddings: Array.isArray(params.textToEmbed) ? [] : [],\n modelName: 'Unknown',\n vectorDimensions: 0,\n error: e.toString()\n };\n }\n }\n\n}\n\n/**\n * Output type for GetData calls - contains results from executing multiple SQL queries\n */\nexport class GetDataOutput {\n /**\n * Indicates if the operation was successful overall. If any individual query failed, this will be false. However, any successful queries will still be returned in the Results array.\n */\n Success: boolean;\n /**\n * The original input of Queries that were run - same order as provided in the request\n */\n Queries: string[];\n /**\n * An ordered array of error messages for each query that was run. This array will always have the same # of entries as Queries. If a query was successful, the corresponding entry will be null.\n */\n ErrorMessages: (string | null)[];\n /**\n * An ordered array of results for each query that was run. This array will always have the same # of entries as Queries. If a query failed, the corresponding entry will be null. Successful results are JSON strings containing the query data.\n */\n Results: (string | null)[];\n}\n\n/**\n * Return type for calls to the GetAllRemoteEntities query - provides lightweight entity metadata\n */\nexport class SimpleRemoteEntityOutput {\n /**\n * Indicates whether the remote entity retrieval was successful\n */\n Success: boolean;\n /**\n * Error message if the operation failed, undefined if successful\n */\n ErrorMessage?: string;\n /**\n * An array of simple entity types that are returned from the remote server - contains basic metadata for each entity\n */\n Results: SimpleRemoteEntity[];\n}\n\n/**\n * Represents a simple entity type that is used for lightweight retrieval of partial remote entity metadata \n */\nexport class SimpleRemoteEntity {\n /**\n * Unique identifier of the entity on the remote server\n */\n ID: string;\n /**\n * Display name of the entity (e.g., \"Users\", \"Companies\")\n */\n Name: string;\n /**\n * Optional description explaining the entity's purpose\n */\n Description?: string;\n /**\n * Database schema name where the entity resides (e.g., \"dbo\", \"custom\")\n */\n SchemaName: string;\n /**\n * Name of the database view used for reading this entity\n */\n BaseView: string;\n /**\n * Name of the database table used for storing this entity\n */\n BaseTable: string;\n /**\n * Optional code-friendly name for the entity (typically PascalCase)\n */\n CodeName?: string;\n /**\n * Optional TypeScript/JavaScript class name for the entity\n */\n ClassName?: string;\n /**\n * Array of field definitions for this entity\n */\n Fields: SimpleRemoteEntityField[];\n}\n\n/**\n * Represents a field within a remote entity - contains basic field metadata\n */\nexport class SimpleRemoteEntityField {\n /**\n * Unique identifier of the entity field on the remote server\n */\n ID: string;\n /**\n * Field name (e.g., \"FirstName\", \"Email\", \"CreatedAt\")\n */\n Name: string;\n /**\n * Optional description explaining the field's purpose\n */\n Description?: string;\n /**\n * Data type of the field (e.g., \"nvarchar\", \"int\", \"datetime\", \"bit\")\n */\n Type: string;\n /**\n * Whether the field can contain null values\n */\n AllowsNull: boolean;\n /**\n * Maximum length for string fields, -1 for unlimited, 0 for non-string types\n */\n MaxLength: number;\n}\n\n/**\n * Input type for RunViewByNameSystemUser method calls - executes a saved view by name\n */\nexport interface RunViewByNameSystemUserInput {\n /**\n * Name of the saved view to execute\n */\n ViewName: string;\n /**\n * Additional WHERE clause conditions to apply (optional)\n */\n ExtraFilter?: string;\n /**\n * ORDER BY clause for sorting results (optional)\n */\n OrderBy?: string;\n /**\n * Specific fields to return, if not specified returns all fields (optional)\n */\n Fields?: string[];\n /**\n * Search string to filter results across searchable fields (optional)\n */\n UserSearchString?: string;\n /**\n * ID of a previous view run to exclude results from (optional)\n */\n ExcludeUserViewRunID?: string;\n /**\n * Override the exclude filter with custom logic (optional)\n */\n OverrideExcludeFilter?: string;\n /**\n * Whether to save the view execution results for future reference (optional)\n */\n SaveViewResults?: boolean;\n /**\n * Whether to exclude data from all prior view runs (optional)\n */\n ExcludeDataFromAllPriorViewRuns?: boolean;\n /**\n * Whether to ignore the view's MaxRows setting and return all results (optional)\n */\n IgnoreMaxRows?: boolean;\n /**\n * Maximum number of rows to return, overrides view setting if specified (optional)\n */\n MaxRows?: number;\n /**\n * Whether to force audit logging for this view execution (optional)\n */\n ForceAuditLog?: boolean;\n /**\n * Description for the audit log entry if ForceAuditLog is true (optional)\n */\n AuditLogDescription?: string;\n /**\n * Type of result format: \"simple\", \"entity_object\", etc. (optional)\n */\n ResultType?: string;\n /**\n * Starting row number for pagination (optional, 0-based)\n */\n StartRow?: number;\n}\n\n/**\n * Input type for RunViewByIDSystemUser method calls - executes a saved view by its unique ID\n */\nexport interface RunViewByIDSystemUserInput {\n /**\n * Unique identifier of the saved view to execute\n */\n ViewID: string;\n /**\n * Additional WHERE clause conditions to apply (optional)\n */\n ExtraFilter?: string;\n /**\n * ORDER BY clause for sorting results (optional)\n */\n OrderBy?: string;\n /**\n * Specific fields to return, if not specified returns all fields (optional)\n */\n Fields?: string[];\n /**\n * Search string to filter results across searchable fields (optional)\n */\n UserSearchString?: string;\n /**\n * ID of a previous view run to exclude results from (optional)\n */\n ExcludeUserViewRunID?: string;\n /**\n * Override the exclude filter with custom logic (optional)\n */\n OverrideExcludeFilter?: string;\n /**\n * Whether to save the view execution results for future reference (optional)\n */\n SaveViewResults?: boolean;\n /**\n * Whether to exclude data from all prior view runs (optional)\n */\n ExcludeDataFromAllPriorViewRuns?: boolean;\n /**\n * Whether to ignore the view's MaxRows setting and return all results (optional)\n */\n IgnoreMaxRows?: boolean;\n /**\n * Maximum number of rows to return, overrides view setting if specified (optional)\n */\n MaxRows?: number;\n /**\n * Whether to force audit logging for this view execution (optional)\n */\n ForceAuditLog?: boolean;\n /**\n * Description for the audit log entry if ForceAuditLog is true (optional)\n */\n AuditLogDescription?: string;\n /**\n * Type of result format: \"simple\", \"entity_object\", etc. (optional)\n */\n ResultType?: string;\n /**\n * Starting row number for pagination (optional, 0-based)\n */\n StartRow?: number;\n}\n\n/**\n * Input type for RunDynamicViewSystemUser method calls - creates and executes a view dynamically based on entity\n */\nexport interface RunDynamicViewSystemUserInput {\n /**\n * Name of the entity to query (e.g., \"Users\", \"Companies\")\n */\n EntityName: string;\n /**\n * Additional WHERE clause conditions to apply (optional)\n */\n ExtraFilter?: string;\n /**\n * ORDER BY clause for sorting results (optional)\n */\n OrderBy?: string;\n /**\n * Specific fields to return, if not specified returns all fields (optional)\n */\n Fields?: string[];\n /**\n * Search string to filter results across searchable fields (optional)\n */\n UserSearchString?: string;\n /**\n * ID of a previous view run to exclude results from (optional)\n */\n ExcludeUserViewRunID?: string;\n /**\n * Override the exclude filter with custom logic (optional)\n */\n OverrideExcludeFilter?: string;\n /**\n * Whether to ignore MaxRows limits and return all results (optional)\n */\n IgnoreMaxRows?: boolean;\n /**\n * Maximum number of rows to return (optional)\n */\n MaxRows?: number;\n /**\n * Whether to force audit logging for this view execution (optional)\n */\n ForceAuditLog?: boolean;\n /**\n * Description for the audit log entry if ForceAuditLog is true (optional)\n */\n AuditLogDescription?: string;\n /**\n * Type of result format: \"simple\", \"entity_object\", etc. (optional)\n */\n ResultType?: string;\n /**\n * Starting row number for pagination (optional, 0-based)\n */\n StartRow?: number;\n}\n\n/**\n * Input type for RunViewsSystemUser method calls - executes multiple views in parallel\n */\nexport interface RunViewSystemUserInput {\n /**\n * Name of the entity to query (e.g., \"Users\", \"Companies\")\n */\n EntityName: string;\n /**\n * Additional WHERE clause conditions to apply (optional)\n */\n ExtraFilter?: string;\n /**\n * ORDER BY clause for sorting results (optional)\n */\n OrderBy?: string;\n /**\n * Specific fields to return, if not specified returns all fields (optional)\n */\n Fields?: string[];\n /**\n * Search string to filter results across searchable fields (optional)\n */\n UserSearchString?: string;\n /**\n * ID of a previous view run to exclude results from (optional)\n */\n ExcludeUserViewRunID?: string;\n /**\n * Override the exclude filter with custom logic (optional)\n */\n OverrideExcludeFilter?: string;\n /**\n * Whether to ignore MaxRows limits and return all results (optional)\n */\n IgnoreMaxRows?: boolean;\n /**\n * Maximum number of rows to return (optional)\n */\n MaxRows?: number;\n /**\n * Whether to force audit logging for this view execution (optional)\n */\n ForceAuditLog?: boolean;\n /**\n * Description for the audit log entry if ForceAuditLog is true (optional)\n */\n AuditLogDescription?: string;\n /**\n * Type of result format: \"simple\", \"entity_object\", etc. (optional)\n */\n ResultType?: string;\n /**\n * Starting row number for pagination (optional, 0-based)\n */\n StartRow?: number;\n}\n\n\n/**\n * Result row type for view execution results - represents a single data row\n */\nexport interface RunViewSystemUserResultRow {\n /**\n * Primary key fields and values for the record\n */\n PrimaryKey: KeyValuePair[];\n /**\n * ID of the entity type this record belongs to\n */\n EntityID: string;\n /**\n * JSON string containing the actual record data\n */\n Data: string;\n}\n\n/**\n * Result type for RunViewsSystemUser method calls - contains execution results and metadata\n */\nexport interface RunViewSystemUserResult {\n /**\n * Array of result rows containing the actual data\n */\n Results: RunViewSystemUserResultRow[];\n /**\n * Unique identifier for this view execution run (optional)\n */\n UserViewRunID?: string;\n /**\n * Number of rows returned in this result set (optional)\n */\n RowCount?: number;\n /**\n * Total number of rows available (before pagination) (optional)\n */\n TotalRowCount?: number;\n /**\n * Time taken to execute the view in milliseconds (optional)\n */\n ExecutionTime?: number;\n /**\n * Error message if the execution failed (optional)\n */\n ErrorMessage?: string;\n /**\n * Whether the view execution was successful\n */\n Success: boolean;\n}\n\n/**\n * Result type for query execution methods - contains query results and execution metadata\n */\nexport interface RunQuerySystemUserResult {\n /**\n * Unique identifier of the executed query\n */\n QueryID: string;\n /**\n * Display name of the executed query\n */\n QueryName: string;\n /**\n * Whether the query execution was successful\n */\n Success: boolean;\n /**\n * Query results data (parsed from JSON)\n */\n Results: any;\n /**\n * Number of rows returned by the query\n */\n RowCount: number;\n /**\n * Total number of rows available (before pagination)\n */\n TotalRowCount: number;\n /**\n * Time taken to execute the query in milliseconds\n */\n ExecutionTime: number;\n /**\n * Error message if the query execution failed\n */\n ErrorMessage: string;\n /**\n * JSON string containing the applied parameters (optional)\n */\n AppliedParameters?: string;\n}\n\n/**\n * Input type for GetQueryDataSystemUser method calls - executes a stored query by ID\n */\nexport interface GetQueryDataSystemUserInput {\n /**\n * The ID of the query to execute\n */\n QueryID: string;\n /**\n * Optional category ID filter\n */\n CategoryID?: string;\n /**\n * Optional category path filter (hierarchical path like \"/MJ/AI/Agents/\" or simple name)\n */\n CategoryPath?: string;\n /**\n * Optional parameters for templated queries\n */\n Parameters?: Record<string, any>;\n /**\n * Optional maximum number of rows to return\n */\n MaxRows?: number;\n /**\n * Optional starting row number for pagination\n */\n StartRow?: number;\n}\n\n/**\n * Input type for GetQueryDataByNameSystemUser method calls - executes a stored query by name\n */\nexport interface GetQueryDataByNameSystemUserInput {\n /**\n * The name of the query to execute\n */\n QueryName: string;\n /**\n * Optional category ID filter\n */\n CategoryID?: string;\n /**\n * Optional category path filter (hierarchical path like \"/MJ/AI/Agents/\" or simple name)\n */\n CategoryPath?: string;\n /**\n * Optional parameters for templated queries\n */\n Parameters?: Record<string, any>;\n /**\n * Optional maximum number of rows to return\n */\n MaxRows?: number;\n /**\n * Optional starting row number for pagination\n */\n StartRow?: number;\n}\n\n/**\n * Input type for query permissions to be created with a new query\n */\nexport interface QueryPermissionInput {\n /**\n * Role ID to grant access to\n */\n RoleID: string;\n}\n\n/**\n * Input type for CreateQuery mutation calls - creates a new query with optional hierarchical category path\n */\nexport interface CreateQueryInput {\n /**\n * Required name for the query (must be unique within category)\n */\n Name: string;\n /**\n * Optional existing category ID to assign the query to\n */\n CategoryID?: string;\n /**\n * Optional category path for automatic hierarchy creation (e.g., \"Reports/Sales/Monthly\") - takes precedence over CategoryID\n */\n CategoryPath?: string;\n /**\n * Optional natural language question this query answers\n */\n UserQuestion?: string;\n /**\n * Optional general description of what the query does\n */\n Description?: string;\n /**\n * Optional SQL query text to execute (can contain Nunjucks template syntax)\n */\n SQL?: string;\n /**\n * Optional technical documentation for developers\n */\n TechnicalDescription?: string;\n /**\n * Optional original SQL before optimization or modification\n */\n OriginalSQL?: string;\n /**\n * Optional user feedback about the query\n */\n Feedback?: string;\n /**\n * Optional query approval status (defaults to 'Pending')\n */\n Status?: 'Pending' | 'Approved' | 'Rejected' | 'Expired';\n /**\n * Optional quality indicator (higher = better quality, defaults to 0)\n */\n QualityRank?: number;\n /**\n * Optional execution cost indicator (higher = more expensive to run)\n */\n ExecutionCostRank?: number;\n /**\n * Optional flag indicating if the query uses Nunjucks template syntax (auto-detected if not specified)\n */\n UsesTemplate?: boolean;\n /**\n * Optional array of permissions to create for the query\n */\n Permissions?: QueryPermissionInput[];\n}\n\n/**\n * Type for query field information\n */\nexport interface QueryField {\n ID: string;\n QueryID: string;\n Name: string;\n Description?: string;\n Sequence: number;\n SQLBaseType?: string;\n SQLFullType?: string;\n SourceEntityID?: string;\n SourceEntity?: string;\n SourceFieldName?: string;\n IsComputed: boolean;\n ComputationDescription?: string;\n IsSummary?: boolean;\n SummaryDescription?: string;\n}\n\n/**\n * Type for query parameter information\n */\nexport interface QueryParameter {\n ID: string;\n QueryID: string;\n Name: string;\n Description?: string;\n Type: string;\n IsRequired: boolean;\n DefaultValue?: string;\n SampleValue?: string;\n ValidationFilters?: string;\n}\n\n/**\n * Type for query entity information\n */\nexport interface MJQueryEntity {\n ID: string;\n QueryID: string;\n EntityID: string;\n Entity?: string;\n}\n\n/**\n * Type for query permission information\n */\nexport interface QueryPermission {\n ID: string;\n QueryID: string;\n RoleID: string;\n Role?: string;\n}\n\n/**\n * Result type for CreateQuery mutation calls - contains creation success status and query data\n */\nexport interface CreateQueryResult {\n /**\n * Whether the query creation was successful\n */\n Success: boolean;\n /**\n * Error message if the creation failed (optional)\n */\n ErrorMessage?: string;\n /**\n * Unique identifier of the created query (optional)\n */\n ID?: string;\n /**\n * Display name of the created query (optional)\n */\n Name?: string;\n /**\n * Description of the created query (optional)\n */\n Description?: string;\n /**\n * Category ID the query belongs to (optional)\n */\n CategoryID?: string;\n /**\n * Category name the query belongs to (optional)\n */\n Category?: string;\n /**\n * SQL query text (optional)\n */\n SQL?: string;\n /**\n * Query status: Pending, Approved, Rejected, or Expired (optional)\n */\n Status?: string;\n /**\n * Quality rank indicator (optional)\n */\n QualityRank?: number;\n /**\n * Embedding vector for semantic search (optional)\n */\n EmbeddingVector?: string;\n /**\n * ID of the embedding model used (optional)\n */\n EmbeddingModelID?: string;\n /**\n * Name of the embedding model used (optional)\n */\n EmbeddingModelName?: string;\n /**\n * Array of fields discovered in the query (optional)\n */\n Fields?: QueryField[];\n /**\n * Array of parameters found in the query template (optional)\n */\n Parameters?: QueryParameter[];\n /**\n * Array of entities referenced by the query (optional)\n */\n Entities?: MJQueryEntity[];\n /**\n * Array of permissions created for the query (optional)\n */\n Permissions?: QueryPermission[];\n}\n\n/**\n * Input type for UpdateQuery mutation calls - updates an existing query\n */\nexport interface UpdateQueryInput {\n /**\n * Required ID of the query to update\n */\n ID: string;\n /**\n * Optional name for the query (must be unique within category)\n */\n Name?: string;\n /**\n * Optional category ID to move the query to\n */\n CategoryID?: string;\n /**\n * Optional category path for automatic hierarchy creation (e.g., \"Reports/Sales/Monthly\") - takes precedence over CategoryID\n */\n CategoryPath?: string;\n /**\n * Optional natural language question this query answers\n */\n UserQuestion?: string;\n /**\n * Optional general description of what the query does\n */\n Description?: string;\n /**\n * Optional SQL query text to execute (can contain Nunjucks template syntax)\n */\n SQL?: string;\n /**\n * Optional technical documentation for developers\n */\n TechnicalDescription?: string;\n /**\n * Optional original SQL before optimization or modification\n */\n OriginalSQL?: string;\n /**\n * Optional user feedback about the query\n */\n Feedback?: string;\n /**\n * Optional query approval status\n */\n Status?: 'Pending' | 'Approved' | 'Rejected' | 'Expired';\n /**\n * Optional quality indicator (higher = better quality)\n */\n QualityRank?: number;\n /**\n * Optional execution cost indicator (higher = more expensive to run)\n */\n ExecutionCostRank?: number;\n /**\n * Optional flag indicating if the query uses Nunjucks template syntax\n */\n UsesTemplate?: boolean;\n /**\n * Optional array of permissions to update for the query (replaces existing permissions)\n */\n Permissions?: QueryPermissionInput[];\n}\n\n/**\n * Result type for UpdateQuery mutation calls - contains update success status and query data\n */\nexport interface UpdateQueryResult {\n /**\n * Whether the query update was successful\n */\n Success: boolean;\n /**\n * Error message if the update failed (optional)\n */\n ErrorMessage?: string;\n /**\n * Unique identifier of the updated query (optional)\n */\n ID?: string;\n /**\n * Display name of the updated query (optional)\n */\n Name?: string;\n /**\n * Description of the updated query (optional)\n */\n Description?: string;\n /**\n * Category ID the query belongs to (optional)\n */\n CategoryID?: string;\n /**\n * Category name the query belongs to (optional)\n */\n Category?: string;\n /**\n * SQL query text (optional)\n */\n SQL?: string;\n /**\n * Query status: Pending, Approved, Rejected, or Expired (optional)\n */\n Status?: string;\n /**\n * Quality rank indicator (optional)\n */\n QualityRank?: number;\n /**\n * Embedding vector for semantic search (optional)\n */\n EmbeddingVector?: string;\n /**\n * ID of the embedding model used (optional)\n */\n EmbeddingModelID?: string;\n /**\n * Name of the embedding model used (optional)\n */\n EmbeddingModelName?: string;\n /**\n * Array of fields discovered in the query (optional)\n */\n Fields?: QueryField[];\n /**\n * Array of parameters found in the query template (optional)\n */\n Parameters?: QueryParameter[];\n /**\n * Array of entities referenced by the query (optional)\n */\n Entities?: MJQueryEntity[];\n /**\n * Array of permissions for the query (optional)\n */\n Permissions?: QueryPermission[];\n}\n\n/**\n * Delete options input type for controlling delete behavior.\n * All fields are optional - defaults will be applied if not provided.\n */\nexport interface DeleteQueryOptionsInput {\n /**\n * Whether to skip AI actions during deletion.\n * @default false\n */\n SkipEntityAIActions?: boolean;\n /**\n * Whether to skip regular entity actions during deletion.\n * @default false\n */\n SkipEntityActions?: boolean;\n /**\n * When true, bypasses Validate() and actual database deletion but still\n * invokes associated actions (AI Actions, Entity Actions, etc.).\n * Used for replaying/simulating delete operations.\n * @default false\n */\n ReplayOnly?: boolean;\n /**\n * When true, indicates this entity is being deleted as part of an IS-A parent chain\n * initiated by a child entity.\n * @default false\n */\n IsParentEntityDelete?: boolean;\n}\n\n/**\n * Result type for DeleteQuery mutation calls - contains deletion success status and deleted query data\n */\nexport interface DeleteQueryResult {\n /**\n * Whether the query deletion was successful\n */\n Success: boolean;\n /**\n * Error message if the deletion failed (optional)\n */\n ErrorMessage?: string;\n /**\n * Unique identifier of the deleted query (optional)\n */\n ID?: string;\n /**\n * Display name of the deleted query (optional)\n */\n Name?: string;\n}\n\n ","import { ActionParam, ActionResult, EntityActionInvocationParams, EntityActionResult } from \"@memberjunction/actions-base\";\nimport { CompositeKey, LogError } from \"@memberjunction/core\";\nimport { GraphQLDataProvider } from \"./graphQLDataProvider\";\nimport { gql } from \"graphql-request\";\n\n/**\n * Client for executing actions and entity actions through GraphQL.\n * This class provides an easy way to execute actions from a client application,\n * similar to how the ActionEngine and EntityActionEngine work on the server.\n * \n * The GraphQLActionClient follows the same naming convention as other GraphQL clients\n * in the MemberJunction ecosystem, such as GraphQLSystemUserClient.\n * \n * @example\n * ```typescript\n * // Create the client\n * const actionClient = new GraphQLActionClient(graphQLProvider);\n * \n * // Run a regular action\n * const result = await actionClient.RunAction(\"action-id\", [\n * { Name: \"parameter1\", Value: \"value1\", Type: \"Input\" }\n * ]);\n * \n * // Run an entity action\n * const entityActionResult = await actionClient.RunEntityAction({\n * EntityAction: action,\n * InvocationType: { Name: \"SingleRecord\" },\n * EntityObject: entityObject,\n * ContextUser: user\n * });\n * ```\n */\nexport class GraphQLActionClient {\n /**\n * The GraphQLDataProvider instance used to execute GraphQL requests\n * @private\n */\n private _dataProvider: GraphQLDataProvider;\n\n /**\n * Creates a new GraphQLActionClient instance.\n * @param dataProvider The GraphQL data provider to use for queries\n */\n constructor(dataProvider: GraphQLDataProvider) {\n this._dataProvider = dataProvider;\n }\n\n /**\n * Run an action by its ID with the specified parameters.\n * \n * This method invokes an action on the server through GraphQL and returns the result.\n * Action parameters are automatically serialized as needed, and results are deserialized\n * for complex data types.\n * \n * @param actionID The ID of the action to run\n * @param params Optional parameters to pass to the action\n * @param skipActionLog Whether to skip logging the action execution (defaults to false)\n * @returns A Promise that resolves to an ActionResult object containing the result of running the action\n * \n * @example\n * ```typescript\n * const result = await actionClient.RunAction(\"action-id\", [\n * { Name: \"param1\", Value: \"value1\", Type: \"Input\" }\n * ]);\n * \n * if (result.Success) {\n * // Action was successful\n * console.log(result.Message);\n * }\n * ```\n */\n public async RunAction(\n actionID: string, \n params?: ActionParam[], \n skipActionLog: boolean = false\n ): Promise<ActionResult> {\n try {\n // Prepare the input variables\n const serializedParams = this.serializeActionParameters(params);\n const variables = this.createActionVariables(actionID, serializedParams, skipActionLog);\n \n // Execute the mutation\n const result = await this.executeActionMutation(variables);\n \n // Process the result\n return this.processActionResult(result, params);\n } catch (e) {\n return this.handleActionError(e, params);\n }\n }\n\n /**\n * Serializes action parameters to ensure complex objects are properly JSON-encoded\n * @param params The action parameters to serialize\n * @returns The serialized parameters\n * @private\n */\n private serializeActionParameters(params?: ActionParam[]): any[] | undefined {\n if (!params) {\n return undefined;\n }\n\n return params.map(p => {\n let value = p.Value;\n if (value !== null && value !== undefined && typeof value === 'object') {\n value = JSON.stringify(value);\n }\n return {\n ...p,\n Value: value\n };\n });\n }\n\n /**\n * Creates the variables for the action mutation\n * @param actionID The ID of the action to run\n * @param params The serialized action parameters\n * @param skipActionLog Whether to skip action logging\n * @returns The action variables\n * @private\n */\n private createActionVariables(\n actionID: string, \n params?: any[], \n skipActionLog: boolean = false\n ): any {\n return {\n input: {\n ActionID: actionID,\n Params: params,\n SkipActionLog: skipActionLog\n }\n };\n }\n\n /**\n * Executes the action mutation\n * @param variables The variables for the mutation\n * @returns The result of the mutation\n * @private\n */\n private async executeActionMutation(variables: any): Promise<any> {\n const mutation = gql`\n mutation RunAction($input: RunActionInput!) {\n RunAction(input: $input) {\n Success\n Message\n ResultCode\n ResultData\n }\n }\n `;\n\n return await this._dataProvider.ExecuteGQL(mutation, variables);\n }\n\n /**\n * Processes the result of an action\n * @param result The result from the GraphQL query\n * @param originalParams The original parameters passed to the action\n * @returns The processed action result\n * @private\n */\n private processActionResult(result: any, originalParams?: ActionParam[]): ActionResult {\n if (!result?.RunAction) {\n throw new Error(\"Invalid response from server\");\n }\n\n // Parse the ResultData if it exists\n let resultData = undefined;\n try {\n if (result.RunAction.ResultData) {\n resultData = JSON.parse(result.RunAction.ResultData);\n }\n } catch (e) {\n LogError(`Failed to parse action result data: ${e}`);\n }\n\n // Return a properly formatted ActionResult\n return {\n Success: result.RunAction.Success,\n Message: result.RunAction.Message,\n Result: resultData,\n LogEntry: null, // We don't return the log entry to clients\n Params: originalParams || [],\n RunParams: null // We don't return the run params to clients\n };\n }\n\n /**\n * Handles errors in the action execution\n * @param e The error\n * @param originalParams The original parameters passed to the action\n * @returns An error result\n * @private\n */\n private handleActionError(e: unknown, originalParams?: ActionParam[]): ActionResult {\n const error = e as Error;\n LogError(`Error running action: ${error}`);\n return {\n Success: false,\n Message: `Error: ${error.message}`,\n Result: null,\n LogEntry: null,\n Params: originalParams || [],\n RunParams: null\n };\n }\n\n /**\n * Run an entity action with the specified parameters.\n * \n * This method invokes an entity action on the server through GraphQL and returns the result.\n * Entity actions are operations that can be performed on entity records, such as validation,\n * business logic, or custom processing. They can operate on a single record, a view, or a list.\n * \n * @param params The parameters for the entity action, including the action to run, \n * invocation type, entity object or view/list IDs, and optional parameters\n * @returns A Promise that resolves to an EntityActionResult object containing the result\n * \n * @example\n * ```typescript\n * // Run an entity action on a single record\n * const result = await actionClient.RunEntityAction({\n * EntityAction: action,\n * InvocationType: { Name: \"SingleRecord\" },\n * EntityObject: entityObject,\n * ContextUser: user\n * });\n * \n * // Run an entity action on a view\n * const viewResult = await actionClient.RunEntityAction({\n * EntityAction: action,\n * InvocationType: { Name: \"View\" },\n * ViewID: \"view-id\",\n * ContextUser: user\n * });\n * ```\n */\n public async RunEntityAction(params: EntityActionInvocationParams): Promise<EntityActionResult> {\n try {\n // Create the GraphQL input\n const input = this.createEntityActionInput(params);\n\n // Execute the GraphQL mutation\n const result = await this.executeEntityActionMutation(input);\n\n // Process the result\n return this.processEntityActionResult(result);\n } catch (e) {\n return this.handleEntityActionError(e);\n }\n }\n\n /**\n * Creates the GraphQL input for an entity action\n * @param params The entity action parameters\n * @returns The GraphQL input\n * @private\n */\n private createEntityActionInput(params: EntityActionInvocationParams): any {\n const input: any = {\n EntityActionID: params.EntityAction.ID,\n InvocationType: params.InvocationType.Name,\n ListID: params.ListID,\n ViewID: params.ViewID,\n };\n \n // Add parameters if available\n if ((params as any).Params) {\n input.Params = this.convertActionParams((params as any).Params);\n }\n \n // Add entity information if available\n if (params.EntityObject) {\n this.addEntityInformation(input, params.EntityObject);\n }\n\n return input;\n }\n\n /**\n * Converts action parameters to the format expected by the GraphQL API\n * @param params The action parameters\n * @returns The converted parameters\n * @private\n */\n private convertActionParams(params: any[]): any[] {\n return params.map(p => {\n let value = p.Value;\n if (value !== null && value !== undefined && typeof value === 'object') {\n value = JSON.stringify(value);\n }\n return {\n Name: p.Name,\n Value: value,\n Type: p.Type\n };\n });\n }\n\n /**\n * Adds entity information to the input object\n * @param input The input object to add to\n * @param entityObject The entity object\n * @private\n */\n private addEntityInformation(input: any, entityObject: any): void {\n // Prefer using entity name instead of ID for better code readability\n input.EntityName = entityObject.EntityInfo?.Name;\n \n // Convert the entity's primary key to the expected format\n if (entityObject.PrimaryKey) {\n input.PrimaryKey = this.convertPrimaryKey(entityObject.PrimaryKey);\n }\n }\n\n /**\n * Converts a primary key object to the format expected by the GraphQL API\n * @param primaryKey The primary key object\n * @returns The converted primary key\n * @private\n */\n private convertPrimaryKey(primaryKey: any): any {\n return {\n KeyValuePairs: primaryKey.KeyValuePairs.map(kvp => this.convertKeyValuePair(kvp))\n };\n }\n\n /**\n * Converts a key-value pair to a string format\n * @param kvp The key-value pair\n * @returns The converted key-value pair\n * @private\n */\n private convertKeyValuePair(kvp: any): any {\n return {\n FieldName: kvp.FieldName,\n Value: kvp.Value !== null && kvp.Value !== undefined ? \n (typeof kvp.Value === 'object' ? JSON.stringify(kvp.Value) : kvp.Value.toString()) \n : null\n };\n }\n\n /**\n * Executes the GraphQL mutation for an entity action\n * @param input The GraphQL input\n * @returns The GraphQL result\n * @private\n */\n private async executeEntityActionMutation(input: any): Promise<any> {\n const mutation = gql`\n mutation RunEntityAction($input: EntityActionInput!) {\n RunEntityAction(input: $input) {\n Success\n Message\n ResultData\n }\n }\n `;\n\n return await this._dataProvider.ExecuteGQL(mutation, { input });\n }\n\n /**\n * Processes the result of an entity action\n * @param result The GraphQL result\n * @returns The processed entity action result\n * @private\n */\n private processEntityActionResult(result: any): EntityActionResult {\n if (!result?.RunEntityAction) {\n throw new Error(\"Invalid response from server\");\n }\n\n // Parse the ResultData\n let resultData = {};\n try {\n if (result.RunEntityAction.ResultData) {\n resultData = JSON.parse(result.RunEntityAction.ResultData);\n }\n } catch (e) {\n LogError(`Failed to parse entity action result data: ${e}`);\n }\n\n // Return a properly formatted EntityActionResult\n return {\n Success: result.RunEntityAction.Success,\n Message: result.RunEntityAction.Message,\n RunParams: null, // We don't return run params to clients\n LogEntry: null, // We don't return the log entry to clients\n ...resultData\n };\n }\n\n /**\n * Handles errors in the entity action\n * @param e The error\n * @returns An error result\n * @private\n */\n private handleEntityActionError(e: unknown): EntityActionResult {\n const error = e as Error;\n LogError(`Error running entity action: ${error}`);\n return {\n Success: false,\n Message: `Error: ${error.message}`,\n RunParams: null,\n LogEntry: null\n };\n }\n}","import { LogError } from \"@memberjunction/core\";\nimport { GraphQLDataProvider } from \"./graphQLDataProvider\";\nimport { gql } from \"graphql-request\";\n\n/**\n * Result of creating an API key\n */\nexport interface CreateAPIKeyResult {\n /** Whether the operation succeeded */\n Success: boolean;\n /** The raw API key - show once and cannot be recovered */\n RawKey?: string;\n /** The database ID of the created key */\n APIKeyID?: string;\n /** Error message if operation failed */\n Error?: string;\n}\n\n/**\n * Parameters for creating an API key\n */\nexport interface CreateAPIKeyParams {\n /** Human-readable label for the key */\n Label: string;\n /** Optional description */\n Description?: string;\n /** Optional expiration date */\n ExpiresAt?: Date;\n /** Optional scope IDs to assign */\n ScopeIDs?: string[];\n}\n\n/**\n * Result of revoking an API key\n */\nexport interface RevokeAPIKeyResult {\n /** Whether the operation succeeded */\n Success: boolean;\n /** Error message if operation failed */\n Error?: string;\n}\n\n/**\n * Client for encryption-related GraphQL operations.\n *\n * This client provides methods for operations that require server-side\n * cryptographic processing, such as API key generation. These operations\n * cannot be performed client-side because they require secure random\n * number generation and cryptographic hashing that must match the\n * server's validation logic.\n *\n * @example\n * ```typescript\n * // Create the client\n * const encryptionClient = new GraphQLEncryptionClient(graphQLProvider);\n *\n * // Create a new API key\n * const result = await encryptionClient.CreateAPIKey({\n * Label: 'My Integration Key',\n * Description: 'Used for external service access',\n * ExpiresAt: new Date('2025-12-31'),\n * ScopeIDs: ['scope-id-1', 'scope-id-2']\n * });\n *\n * if (result.Success) {\n * // Show rawKey to user ONCE - cannot be recovered\n * console.log('Save this key:', result.RawKey);\n * }\n * ```\n */\nexport class GraphQLEncryptionClient {\n /**\n * The GraphQLDataProvider instance used to execute GraphQL requests\n */\n private _dataProvider: GraphQLDataProvider;\n\n /**\n * Creates a new GraphQLEncryptionClient instance.\n * @param dataProvider The GraphQL data provider to use for queries\n */\n constructor(dataProvider: GraphQLDataProvider) {\n this._dataProvider = dataProvider;\n }\n\n /**\n * Creates a new API key with secure server-side cryptographic hashing.\n *\n * This method calls the server to:\n * 1. Generate a cryptographically secure random API key\n * 2. Hash the key using SHA-256 for secure storage\n * 3. Store only the hash in the database\n * 4. Return the raw key ONCE\n *\n * **CRITICAL**: The raw key is returned only once and cannot be recovered.\n * Instruct users to save it immediately in a secure location.\n *\n * @param params Configuration for the new API key\n * @returns Result with raw key (show once!) and database ID\n *\n * @example\n * ```typescript\n * const result = await client.CreateAPIKey({\n * Label: 'Production Integration',\n * Description: 'API access for our CRM system',\n * ExpiresAt: new Date('2025-12-31'),\n * ScopeIDs: ['entities:read', 'entities:write']\n * });\n *\n * if (result.Success) {\n * alert(`Save this key now! It won't be shown again:\\n${result.RawKey}`);\n * } else {\n * console.error('Failed to create key:', result.Error);\n * }\n * ```\n */\n public async CreateAPIKey(params: CreateAPIKeyParams): Promise<CreateAPIKeyResult> {\n try {\n const variables = this.createAPIKeyVariables(params);\n const result = await this.executeCreateAPIKeyMutation(variables);\n return this.processCreateAPIKeyResult(result);\n } catch (e) {\n return this.handleCreateAPIKeyError(e);\n }\n }\n\n /**\n * Creates the variables for the CreateAPIKey mutation\n * @param params The API key creation parameters\n * @returns The mutation variables\n */\n private createAPIKeyVariables(params: CreateAPIKeyParams): Record<string, unknown> {\n return {\n input: {\n Label: params.Label,\n Description: params.Description,\n ExpiresAt: params.ExpiresAt?.toISOString(),\n ScopeIDs: params.ScopeIDs\n }\n };\n }\n\n /**\n * Executes the CreateAPIKey mutation\n * @param variables The mutation variables\n * @returns The GraphQL result\n */\n private async executeCreateAPIKeyMutation(variables: Record<string, unknown>): Promise<Record<string, unknown>> {\n const mutation = gql`\n mutation CreateAPIKey($input: CreateAPIKeyInput!) {\n CreateAPIKey(input: $input) {\n Success\n RawKey\n APIKeyID\n Error\n }\n }\n `;\n\n return await this._dataProvider.ExecuteGQL(mutation, variables);\n }\n\n /**\n * Processes the result of the CreateAPIKey mutation\n * @param result The GraphQL result\n * @returns The processed result\n */\n private processCreateAPIKeyResult(result: Record<string, unknown>): CreateAPIKeyResult {\n const data = result as { CreateAPIKey?: CreateAPIKeyResult };\n\n if (!data?.CreateAPIKey) {\n return {\n Success: false,\n Error: \"Invalid response from server\"\n };\n }\n\n return {\n Success: data.CreateAPIKey.Success,\n RawKey: data.CreateAPIKey.RawKey,\n APIKeyID: data.CreateAPIKey.APIKeyID,\n Error: data.CreateAPIKey.Error\n };\n }\n\n /**\n * Handles errors in the CreateAPIKey operation\n * @param e The error\n * @returns An error result\n */\n private handleCreateAPIKeyError(e: unknown): CreateAPIKeyResult {\n const error = e as Error;\n LogError(`Error creating API key: ${error.message}`);\n return {\n Success: false,\n Error: `Error: ${error.message}`\n };\n }\n\n /**\n * Revokes an API key, permanently disabling it.\n *\n * Once revoked, an API key cannot be reactivated. Users must create a new key.\n *\n * @param apiKeyId The database ID of the API key to revoke\n * @returns Result indicating success or failure\n *\n * @example\n * ```typescript\n * const result = await client.RevokeAPIKey('key-uuid-here');\n *\n * if (result.Success) {\n * console.log('API key has been revoked');\n * } else {\n * console.error('Failed to revoke:', result.Error);\n * }\n * ```\n */\n public async RevokeAPIKey(apiKeyId: string): Promise<RevokeAPIKeyResult> {\n try {\n const mutation = gql`\n mutation RevokeAPIKey($apiKeyId: String!) {\n RevokeAPIKey(apiKeyId: $apiKeyId) {\n Success\n Error\n }\n }\n `;\n\n const result = await this._dataProvider.ExecuteGQL(mutation, { apiKeyId });\n const data = result as { RevokeAPIKey?: RevokeAPIKeyResult };\n\n if (!data?.RevokeAPIKey) {\n return {\n Success: false,\n Error: \"Invalid response from server\"\n };\n }\n\n return {\n Success: data.RevokeAPIKey.Success,\n Error: data.RevokeAPIKey.Error\n };\n } catch (e) {\n const error = e as Error;\n LogError(`Error revoking API key: ${error.message}`);\n return {\n Success: false,\n Error: `Error: ${error.message}`\n };\n }\n }\n}\n","import { LogError } from \"@memberjunction/core\";\nimport { GraphQLDataProvider } from \"./graphQLDataProvider\";\nimport { gql } from \"graphql-request\";\nimport { SafeJSONParse } from \"@memberjunction/global\";\n\n/**\n * Parameters for running a test\n */\nexport interface RunTestParams {\n testId: string;\n verbose?: boolean;\n environment?: string;\n tags?: string[];\n /**\n * Variable values to use for this test run.\n * Key is the variable name, value is the resolved value.\n */\n variables?: Record<string, unknown>;\n onProgress?: (progress: TestExecutionProgress) => void;\n}\n\n/**\n * Result from running a test\n */\nexport interface RunTestResult {\n success: boolean;\n errorMessage?: string;\n executionTimeMs?: number;\n result: any; // Parsed TestRunResult from engine\n}\n\n/**\n * Parameters for running a test suite\n */\nexport interface RunTestSuiteParams {\n suiteId: string;\n verbose?: boolean;\n environment?: string;\n parallel?: boolean;\n tags?: string[];\n /**\n * Variable values to apply to all tests in this suite.\n * Key is the variable name, value is the resolved value.\n */\n variables?: Record<string, unknown>;\n /**\n * Run only specific tests by their IDs.\n * If provided, only tests with matching IDs will be executed.\n */\n selectedTestIds?: string[];\n /**\n * Start execution from this sequence number (inclusive).\n * Tests with sequence numbers less than this value will be skipped.\n */\n sequenceStart?: number;\n /**\n * Stop execution at this sequence number (inclusive).\n * Tests with sequence numbers greater than this value will be skipped.\n */\n sequenceEnd?: number;\n onProgress?: (progress: TestExecutionProgress) => void;\n}\n\n/**\n * Result from running a test suite\n */\nexport interface RunTestSuiteResult {\n success: boolean;\n errorMessage?: string;\n executionTimeMs?: number;\n result: any; // Parsed TestSuiteRunResult from engine\n}\n\n/**\n * Test execution progress update\n */\nexport interface TestExecutionProgress {\n currentStep: string;\n percentage: number;\n message: string;\n testName?: string;\n driverType?: string;\n oracleEvaluation?: string;\n}\n\n/**\n * Client for executing tests through GraphQL.\n * This class provides an easy way to run tests and test suites from a client application.\n *\n * @example\n * ```typescript\n * // Create the client\n * const testingClient = new GraphQLTestingClient(graphQLProvider);\n *\n * // Run a test\n * const result = await testingClient.RunTest({\n * testId: \"test-uuid\",\n * verbose: true,\n * environment: \"dev\"\n * });\n *\n * // Run a test suite\n * const suiteResult = await testingClient.RunTestSuite({\n * suiteId: \"suite-uuid\",\n * parallel: true\n * });\n * ```\n */\nexport class GraphQLTestingClient {\n private _dataProvider: GraphQLDataProvider;\n\n /**\n * Creates a new GraphQLTestingClient instance.\n * @param dataProvider The GraphQL data provider to use for queries\n */\n constructor(dataProvider: GraphQLDataProvider) {\n this._dataProvider = dataProvider;\n }\n\n /**\n * Run a single test with the specified parameters.\n *\n * This method invokes a test on the server through GraphQL and returns the result.\n * If a progress callback is provided in params.onProgress, this method will subscribe\n * to real-time progress updates from the GraphQL server and forward them to the callback.\n *\n * @param params The parameters for running the test\n * @returns A Promise that resolves to a RunTestResult object\n *\n * @example\n * ```typescript\n * const result = await testingClient.RunTest({\n * testId: \"test-uuid\",\n * verbose: true,\n * environment: \"staging\",\n * onProgress: (progress) => {\n * console.log(`${progress.currentStep}: ${progress.message} (${progress.percentage}%)`);\n * }\n * });\n *\n * if (result.success) {\n * console.log('Test passed!', result.result);\n * } else {\n * console.error('Test failed:', result.errorMessage);\n * }\n * ```\n */\n public async RunTest(params: RunTestParams): Promise<RunTestResult> {\n let subscription: any;\n\n try {\n // Subscribe to progress updates if callback provided\n if (params.onProgress) {\n subscription = this._dataProvider.PushStatusUpdates(this._dataProvider.sessionId)\n .subscribe((message: string) => {\n try {\n const parsed = JSON.parse(message);\n\n // Filter for TestExecutionProgress messages from RunTestResolver\n if (parsed.resolver === 'RunTestResolver' &&\n parsed.type === 'TestExecutionProgress' &&\n parsed.status === 'ok' &&\n parsed.data?.progress) {\n\n // Forward progress to callback\n params.onProgress!(parsed.data.progress);\n }\n } catch (e) {\n console.error('[GraphQLTestingClient] Failed to parse progress message:', e);\n }\n });\n }\n\n const mutation = gql`\n mutation RunTest(\n $testId: String!,\n $verbose: Boolean,\n $environment: String,\n $tags: String,\n $variables: String\n ) {\n RunTest(\n testId: $testId,\n verbose: $verbose,\n environment: $environment,\n tags: $tags,\n variables: $variables\n ) {\n success\n errorMessage\n executionTimeMs\n result\n }\n }\n `;\n\n // Serialize tags array to JSON string for GraphQL\n const tagsJson = params.tags && params.tags.length > 0 ? JSON.stringify(params.tags) : undefined;\n // Serialize variables object to JSON string for GraphQL\n const variablesJson = params.variables ? JSON.stringify(params.variables) : undefined;\n\n const variables = {\n testId: params.testId,\n verbose: params.verbose,\n environment: params.environment,\n tags: tagsJson,\n variables: variablesJson\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n return this.processTestResult(result.RunTest);\n\n } catch (e) {\n return this.handleError(e, 'RunTest');\n } finally {\n // Always clean up subscription\n if (subscription) {\n subscription.unsubscribe();\n }\n }\n }\n\n /**\n * Run a test suite with the specified parameters.\n *\n * If a progress callback is provided in params.onProgress, this method will subscribe\n * to real-time progress updates from the GraphQL server and forward them to the callback.\n *\n * @param params The parameters for running the test suite\n * @returns A Promise that resolves to a RunTestSuiteResult object\n *\n * @example\n * ```typescript\n * const result = await testingClient.RunTestSuite({\n * suiteId: \"suite-uuid\",\n * parallel: true,\n * verbose: false,\n * onProgress: (progress) => {\n * console.log(`Progress: ${progress.message} (${progress.percentage}%)`);\n * }\n * });\n *\n * console.log(`Suite: ${result.result.totalTests} tests run`);\n * console.log(`Passed: ${result.result.passedTests}`);\n * ```\n */\n public async RunTestSuite(params: RunTestSuiteParams): Promise<RunTestSuiteResult> {\n let subscription: any;\n\n try {\n // Subscribe to progress updates if callback provided\n if (params.onProgress) {\n subscription = this._dataProvider.PushStatusUpdates(this._dataProvider.sessionId)\n .subscribe((message: string) => {\n try {\n const parsed = JSON.parse(message);\n\n // Filter for TestExecutionProgress messages from RunTestResolver\n if (parsed.resolver === 'RunTestResolver' &&\n parsed.type === 'TestExecutionProgress' &&\n parsed.status === 'ok' &&\n parsed.data?.progress) {\n\n // Forward progress to callback\n params.onProgress!(parsed.data.progress);\n }\n } catch (e) {\n console.error('[GraphQLTestingClient] Failed to parse progress message:', e);\n }\n });\n }\n\n const mutation = gql`\n mutation RunTestSuite(\n $suiteId: String!,\n $verbose: Boolean,\n $environment: String,\n $parallel: Boolean,\n $tags: String,\n $variables: String,\n $selectedTestIds: String,\n $sequenceStart: Int,\n $sequenceEnd: Int\n ) {\n RunTestSuite(\n suiteId: $suiteId,\n verbose: $verbose,\n environment: $environment,\n parallel: $parallel,\n tags: $tags,\n variables: $variables,\n selectedTestIds: $selectedTestIds,\n sequenceStart: $sequenceStart,\n sequenceEnd: $sequenceEnd\n ) {\n success\n errorMessage\n executionTimeMs\n result\n }\n }\n `;\n\n // Serialize tags array to JSON string for GraphQL\n const tagsJson = params.tags && params.tags.length > 0 ? JSON.stringify(params.tags) : undefined;\n // Serialize variables object to JSON string for GraphQL\n const variablesJson = params.variables ? JSON.stringify(params.variables) : undefined;\n // Serialize selectedTestIds array to JSON string for GraphQL\n const selectedTestIdsJson = params.selectedTestIds && params.selectedTestIds.length > 0\n ? JSON.stringify(params.selectedTestIds)\n : undefined;\n\n const variables = {\n suiteId: params.suiteId,\n verbose: params.verbose,\n environment: params.environment,\n parallel: params.parallel,\n tags: tagsJson,\n variables: variablesJson,\n selectedTestIds: selectedTestIdsJson,\n sequenceStart: params.sequenceStart,\n sequenceEnd: params.sequenceEnd\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n return this.processSuiteResult(result.RunTestSuite);\n\n } catch (e) {\n return this.handleError(e, 'RunTestSuite');\n } finally {\n // Always clean up subscription\n if (subscription) {\n subscription.unsubscribe();\n }\n }\n }\n\n /**\n * Check if a test is currently running\n *\n * @param testId The test ID to check\n * @returns True if the test is running, false otherwise\n */\n public async IsTestRunning(testId: string): Promise<boolean> {\n try {\n const query = gql`\n query IsTestRunning($testId: String!) {\n IsTestRunning(testId: $testId)\n }\n `;\n\n const result = await this._dataProvider.ExecuteGQL(query, { testId });\n return result.IsTestRunning;\n\n } catch (e) {\n LogError(`Error checking test running status: ${(e as Error).message}`);\n return false;\n }\n }\n\n // ===== Helper Methods =====\n\n private processTestResult(result: any): RunTestResult {\n if (!result) {\n throw new Error(\"Invalid response from server\");\n }\n\n let parsedResult: any;\n try {\n parsedResult = SafeJSONParse(result.result);\n } catch (e) {\n parsedResult = result.result;\n }\n\n return {\n success: result.success,\n errorMessage: result.errorMessage,\n executionTimeMs: result.executionTimeMs,\n result: parsedResult\n };\n }\n\n private processSuiteResult(result: any): RunTestSuiteResult {\n if (!result) {\n throw new Error(\"Invalid response from server\");\n }\n\n let parsedResult: any;\n try {\n parsedResult = SafeJSONParse(result.result);\n } catch (e) {\n parsedResult = result.result;\n }\n\n return {\n success: result.success,\n errorMessage: result.errorMessage,\n executionTimeMs: result.executionTimeMs,\n result: parsedResult\n };\n }\n\n private handleError(error: any, operation: string): any {\n const errorMsg = (error as Error).message;\n LogError(`${operation} failed: ${errorMsg}`);\n\n return {\n success: false,\n errorMessage: errorMsg,\n result: null\n };\n }\n}\n","import { LogError } from \"@memberjunction/core\";\nimport { GraphQLDataProvider } from \"./graphQLDataProvider\";\nimport { gql } from \"graphql-request\";\nimport { ComponentSpec } from \"@memberjunction/interactive-component-types\";\n\n/**\n * Parameters for getting a component from registry\n */\nexport interface GetRegistryComponentParams {\n /**\n * Registry name (globally unique)\n */\n registryName: string;\n \n /**\n * Component namespace\n */\n namespace: string;\n \n /**\n * Component name\n */\n name: string;\n \n /**\n * Component version (optional, defaults to 'latest')\n */\n version?: string;\n \n /**\n * Optional hash for caching - if provided and matches, returns null\n */\n hash?: string;\n}\n\n/**\n * Response from GetRegistryComponent with hash and caching metadata\n */\nexport interface ComponentSpecWithHash {\n /**\n * The component specification (undefined if not modified)\n */\n specification?: ComponentSpec | string; // Can be either parsed object or JSON string\n \n /**\n * SHA-256 hash of the specification\n */\n hash: string;\n \n /**\n * Indicates if the component was not modified (304 response)\n */\n notModified: boolean;\n \n /**\n * Optional message from server\n */\n message?: string;\n}\n\n/**\n * Parameters for searching registry components\n */\nexport interface SearchRegistryComponentsParams {\n /**\n * Optional registry name filter\n */\n registryName?: string;\n \n /**\n * Optional namespace filter\n */\n namespace?: string;\n \n /**\n * Search query string\n */\n query?: string;\n \n /**\n * Component type filter\n */\n type?: string;\n \n /**\n * Tags to filter by\n */\n tags?: string[];\n \n /**\n * Maximum number of results\n */\n limit?: number;\n \n /**\n * Offset for pagination\n */\n offset?: number;\n}\n\n/**\n * Search result for registry components\n */\nexport interface RegistryComponentSearchResult {\n /**\n * Array of matching components\n */\n components: ComponentSpec[];\n \n /**\n * Total number of matches\n */\n total: number;\n \n /**\n * Current offset\n */\n offset: number;\n \n /**\n * Current limit\n */\n limit: number;\n}\n\n/**\n * Dependency tree for a component\n */\nexport interface ComponentDependencyTree {\n /**\n * Component ID\n */\n componentId: string;\n\n /**\n * Component name\n */\n name?: string;\n\n /**\n * Component namespace\n */\n namespace?: string;\n\n /**\n * Component version\n */\n version?: string;\n\n /**\n * Direct dependencies\n */\n dependencies?: ComponentDependencyTree[];\n\n /**\n * Whether this is a circular dependency\n */\n circular?: boolean;\n\n /**\n * Total count of all dependencies\n */\n totalCount?: number;\n}\n\n/**\n * Input parameters for sending component feedback\n */\nexport interface ComponentFeedbackParams {\n /**\n * Component name\n */\n componentName: string;\n\n /**\n * Component namespace\n */\n componentNamespace: string;\n\n /**\n * Component version (optional)\n */\n componentVersion?: string;\n\n /**\n * Registry name (optional - for registry-specific feedback)\n */\n registryName?: string;\n\n /**\n * Rating (typically 0-5 scale)\n */\n rating: number;\n\n /**\n * Type of feedback (optional)\n */\n feedbackType?: string;\n\n /**\n * User comments (optional)\n */\n comments?: string;\n\n /**\n * Associated conversation ID (optional)\n */\n conversationID?: string;\n\n /**\n * Associated conversation detail ID (optional)\n */\n conversationDetailID?: string;\n\n /**\n * Associated report ID (optional)\n */\n reportID?: string;\n\n /**\n * Associated dashboard ID (optional)\n */\n dashboardID?: string;\n}\n\n/**\n * Response from sending component feedback\n */\nexport interface ComponentFeedbackResponse {\n /**\n * Whether the feedback was successfully submitted\n */\n success: boolean;\n\n /**\n * ID of the created feedback record (if available)\n */\n feedbackID?: string;\n\n /**\n * Error message if submission failed\n */\n error?: string;\n}\n\n/**\n * Client for executing Component Registry operations through GraphQL.\n * This class provides an easy way to fetch components from external registries\n * through the MJ API server, which handles authentication and caching.\n * \n * The GraphQLComponentRegistryClient follows the same naming convention as other GraphQL clients\n * in the MemberJunction ecosystem, such as GraphQLAIClient and GraphQLActionClient.\n * \n * @example\n * ```typescript\n * // Create the client\n * const registryClient = new GraphQLComponentRegistryClient(graphQLProvider);\n * \n * // Get a component from a registry\n * const component = await registryClient.GetRegistryComponent({\n * registryName: \"MJ\",\n * namespace: \"core/ui\",\n * name: \"DataGrid\",\n * version: \"1.0.0\"\n * });\n * \n * // Search for components\n * const searchResult = await registryClient.SearchRegistryComponents({\n * query: \"dashboard\",\n * type: \"dashboard\",\n * limit: 10\n * });\n * ```\n */\nexport class GraphQLComponentRegistryClient {\n /**\n * The GraphQLDataProvider instance used to execute GraphQL requests\n * @private\n */\n private _dataProvider: GraphQLDataProvider;\n\n /**\n * Creates a new GraphQLComponentRegistryClient instance.\n * @param dataProvider The GraphQL data provider to use for queries\n */\n constructor(dataProvider: GraphQLDataProvider) {\n this._dataProvider = dataProvider;\n }\n\n /**\n * Get a specific component from a registry.\n * \n * This method fetches a component specification from an external registry\n * through the MJ API server. The server handles authentication with the\n * registry and may cache the result for performance.\n * \n * @param params The parameters for getting the component\n * @returns A Promise that resolves to a ComponentSpec\n * \n * @example\n * ```typescript\n * const component = await registryClient.GetRegistryComponent({\n * registryName: \"MJ\",\n * namespace: \"core/ui\",\n * name: \"DataGrid\",\n * version: \"2.0.0\"\n * });\n * \n * console.log('Component:', component.name);\n * console.log('Description:', component.description);\n * console.log('Code:', component.code);\n * ```\n */\n public async GetRegistryComponent(params: GetRegistryComponentParams): Promise<ComponentSpec | null> {\n try {\n // Build the query - specification is now a JSON string\n const query = gql`\n query GetRegistryComponent(\n $registryName: String!,\n $namespace: String!,\n $name: String!,\n $version: String,\n $hash: String\n ) {\n GetRegistryComponent(\n registryName: $registryName,\n namespace: $namespace,\n name: $name,\n version: $version,\n hash: $hash\n ) {\n hash\n notModified\n message\n specification\n }\n }\n `;\n\n // Prepare variables\n const variables: Record<string, any> = {\n registryName: params.registryName,\n namespace: params.namespace,\n name: params.name\n };\n\n if (params.version !== undefined) {\n variables.version = params.version;\n }\n \n if (params.hash !== undefined) {\n variables.hash = params.hash;\n }\n\n // Execute the query\n const result = await this._dataProvider.ExecuteGQL(query, variables);\n\n // Handle new response structure with hash\n if (result && result.GetRegistryComponent) {\n const response = result.GetRegistryComponent as ComponentSpecWithHash;\n \n // If not modified and no specification, return null (client should use cache)\n if (response.notModified && !response.specification) {\n return null;\n }\n \n // Parse the JSON string specification if available\n if (response.specification) {\n // If it's already an object, return it\n if (typeof response.specification === 'object') {\n return response.specification as ComponentSpec;\n }\n // Otherwise parse the JSON string\n try {\n return JSON.parse(response.specification) as ComponentSpec;\n } catch (e) {\n LogError(`Failed to parse component specification: ${e}`);\n return null;\n }\n }\n \n return null;\n }\n\n return null;\n } catch (e) {\n LogError(e);\n throw new Error(`Failed to get registry component: ${e instanceof Error ? e.message : 'Unknown error'}`);\n }\n }\n\n /**\n * Get a component from registry with hash and caching metadata.\n * Returns the full response including hash and notModified flag.\n * \n * @param params - Parameters for fetching the component\n * @returns Full response with specification, hash, and caching metadata\n * \n * @example\n * ```typescript\n * const response = await client.GetRegistryComponentWithHash({\n * registryName: 'MJ',\n * namespace: 'core/ui',\n * name: 'DataGrid',\n * version: '1.0.0',\n * hash: 'abc123...'\n * });\n * \n * if (response.notModified) {\n * // Use cached version\n * } else {\n * // Use response.specification\n * }\n * ```\n */\n public async GetRegistryComponentWithHash(params: GetRegistryComponentParams): Promise<ComponentSpecWithHash> {\n try {\n // Build the query - same as GetRegistryComponent\n const query = gql`\n query GetRegistryComponent(\n $registryName: String!,\n $namespace: String!,\n $name: String!,\n $version: String,\n $hash: String\n ) {\n GetRegistryComponent(\n registryName: $registryName,\n namespace: $namespace,\n name: $name,\n version: $version,\n hash: $hash\n ) {\n hash\n notModified\n message\n specification\n }\n }\n `;\n\n // Prepare variables\n const variables: Record<string, any> = {\n registryName: params.registryName,\n namespace: params.namespace,\n name: params.name\n };\n\n if (params.version !== undefined) {\n variables.version = params.version;\n }\n \n if (params.hash !== undefined) {\n variables.hash = params.hash;\n }\n\n // Execute the query\n const result = await this._dataProvider.ExecuteGQL(query, variables);\n\n // Return the full response with parsed specification\n if (result && result.GetRegistryComponent) {\n const response = result.GetRegistryComponent;\n let spec: ComponentSpec | undefined;\n if (response.specification) {\n try {\n spec = JSON.parse(response.specification) as ComponentSpec;\n } catch (e) {\n LogError(`Failed to parse component specification in GetRegistryComponentWithHash: ${e}`);\n spec = undefined;\n }\n }\n return {\n specification: spec,\n hash: response.hash,\n notModified: response.notModified,\n message: response.message\n } as ComponentSpecWithHash;\n }\n\n // Return empty response if nothing found\n return {\n specification: undefined,\n hash: '',\n notModified: false,\n message: 'Component not found'\n };\n } catch (e) {\n LogError(e);\n throw new Error(`Failed to get registry component with hash: ${e instanceof Error ? e.message : 'Unknown error'}`);\n }\n }\n\n /**\n * Search for components in registries.\n * \n * This method searches for components across one or more registries\n * based on the provided criteria. Results are paginated for performance.\n * \n * @param params The search parameters\n * @returns A Promise that resolves to a RegistryComponentSearchResult\n * \n * @example\n * ```typescript\n * const searchResult = await registryClient.SearchRegistryComponents({\n * query: \"dashboard\",\n * type: \"dashboard\",\n * tags: [\"analytics\", \"reporting\"],\n * limit: 20,\n * offset: 0\n * });\n * \n * console.log(`Found ${searchResult.total} components`);\n * searchResult.components.forEach(component => {\n * console.log(`- ${component.name}: ${component.description}`);\n * });\n * ```\n */\n public async SearchRegistryComponents(params: SearchRegistryComponentsParams): Promise<RegistryComponentSearchResult> {\n try {\n // Build the query\n const query = gql`\n query SearchRegistryComponents($params: SearchRegistryComponentsInput!) {\n SearchRegistryComponents(params: $params) {\n components\n total\n offset\n limit\n }\n }\n `;\n\n // Execute the query\n const result = await this._dataProvider.ExecuteGQL(query, { params });\n\n // Return the search result with parsed components\n if (result && result.SearchRegistryComponents) {\n const searchResult = result.SearchRegistryComponents;\n return {\n components: searchResult.components.map((json: string) => JSON.parse(json) as ComponentSpec),\n total: searchResult.total,\n offset: searchResult.offset,\n limit: searchResult.limit\n } as RegistryComponentSearchResult;\n }\n\n return {\n components: [],\n total: 0,\n offset: 0,\n limit: params.limit || 10\n };\n } catch (e) {\n LogError(e);\n throw new Error(`Failed to search registry components: ${e instanceof Error ? e.message : 'Unknown error'}`);\n }\n }\n\n /**\n * Resolve the dependency tree for a component.\n * \n * This method fetches the complete dependency tree for a component,\n * including all transitive dependencies. The server handles circular\n * dependency detection and marks them appropriately.\n * \n * @param registryId The registry ID\n * @param componentId The component ID\n * @returns A Promise that resolves to a ComponentDependencyTree\n * \n * @example\n * ```typescript\n * const dependencyTree = await registryClient.ResolveComponentDependencies(\n * \"mj-central\",\n * \"component-123\"\n * );\n * \n * console.log(`Component has ${dependencyTree.totalCount} total dependencies`);\n * if (dependencyTree.circular) {\n * console.warn('Circular dependency detected!');\n * }\n * ```\n */\n public async ResolveComponentDependencies(\n registryId: string,\n componentId: string\n ): Promise<ComponentDependencyTree | null> {\n try {\n // Build the query\n const query = gql`\n query ResolveComponentDependencies(\n $registryId: String!,\n $componentId: String!\n ) {\n ResolveComponentDependencies(\n registryId: $registryId,\n componentId: $componentId\n ) {\n componentId\n name\n namespace\n version\n circular\n totalCount\n dependencies {\n componentId\n name\n namespace\n version\n circular\n totalCount\n }\n }\n }\n `;\n\n // Execute the query\n const result = await this._dataProvider.ExecuteGQL(query, {\n registryId,\n componentId\n });\n\n // Return the dependency tree\n if (result && result.ResolveComponentDependencies) {\n return result.ResolveComponentDependencies as ComponentDependencyTree;\n }\n\n return null;\n } catch (e) {\n LogError(e);\n throw new Error(`Failed to resolve component dependencies: ${e instanceof Error ? e.message : 'Unknown error'}`);\n }\n }\n\n /**\n * Check if a specific version of a component exists in a registry.\n * \n * @param params The parameters for checking component existence\n * @returns A Promise that resolves to true if the component exists, false otherwise\n * \n * @example\n * ```typescript\n * const exists = await registryClient.ComponentExists({\n * registryId: \"mj-central\",\n * namespace: \"core/ui\",\n * name: \"DataGrid\",\n * version: \"2.0.0\"\n * });\n * \n * if (exists) {\n * console.log('Component is available');\n * }\n * ```\n */\n public async ComponentExists(params: GetRegistryComponentParams): Promise<boolean> {\n try {\n const component = await this.GetRegistryComponent(params);\n return component !== null;\n } catch (e) {\n // If we get an error, assume the component doesn't exist\n return false;\n }\n }\n\n /**\n * Get the latest version of a component.\n * \n * @param registryId The registry ID\n * @param namespace The component namespace\n * @param name The component name\n * @returns A Promise that resolves to the latest version string or null\n * \n * @example\n * ```typescript\n * const latestVersion = await registryClient.GetLatestVersion(\n * \"mj-central\",\n * \"core/ui\",\n * \"DataGrid\"\n * );\n * \n * console.log(`Latest version: ${latestVersion}`);\n * ```\n */\n public async GetLatestVersion(\n registryName: string,\n namespace: string,\n name: string\n ): Promise<string | null> {\n try {\n const component = await this.GetRegistryComponent({\n registryName,\n namespace,\n name,\n version: 'latest'\n });\n\n return component?.version || null;\n } catch (e) {\n LogError(e);\n return null;\n }\n }\n\n /**\n * Send feedback for a component.\n *\n * This is a registry-agnostic method that allows submitting feedback\n * for any component from any registry. The feedback can include ratings,\n * comments, and associations with conversations, reports, or dashboards.\n *\n * @param params The feedback parameters\n * @returns A Promise that resolves to a ComponentFeedbackResponse\n *\n * @example\n * ```typescript\n * const response = await registryClient.SendComponentFeedback({\n * componentName: 'DataGrid',\n * componentNamespace: 'core/ui',\n * componentVersion: '1.0.0',\n * registryName: 'MJ',\n * rating: 5,\n * feedbackType: 'feature-request',\n * comments: 'Would love to see export to Excel functionality',\n * conversationID: 'conv-123'\n * });\n *\n * if (response.success) {\n * console.log('Feedback submitted successfully!');\n * if (response.feedbackID) {\n * console.log(`Feedback ID: ${response.feedbackID}`);\n * }\n * } else {\n * console.error('Feedback submission failed:', response.error);\n * }\n * ```\n */\n public async SendComponentFeedback(params: ComponentFeedbackParams): Promise<ComponentFeedbackResponse> {\n try {\n // Build the mutation\n const mutation = gql`\n mutation SendComponentFeedback($feedback: ComponentFeedbackInput!) {\n SendComponentFeedback(feedback: $feedback) {\n success\n feedbackID\n error\n }\n }\n `;\n\n // Execute the mutation\n const result = await this._dataProvider.ExecuteGQL(mutation, { feedback: params });\n\n // Return the response\n if (result && result.SendComponentFeedback) {\n return result.SendComponentFeedback as ComponentFeedbackResponse;\n }\n\n return {\n success: false,\n error: 'No response from server'\n };\n } catch (e) {\n LogError(e);\n return {\n success: false,\n error: e instanceof Error ? e.message : 'Unknown error'\n };\n }\n }\n}","import { LogError } from '@memberjunction/core';\nimport { GraphQLDataProvider } from './graphQLDataProvider';\nimport { gql } from 'graphql-request';\n\n// =========================================================================\n// Client-side params / result types\n// =========================================================================\n\n/**\n * Progress update received during label creation.\n */\nexport interface CreateVersionLabelProgress {\n /** Current lifecycle step */\n Step: 'initializing' | 'walking_dependencies' | 'capturing_snapshots' | 'finalizing';\n /** Human-readable description of what's happening */\n Message: string;\n /** Estimated completion percentage (0–100) */\n Percentage: number;\n /** Number of records processed so far */\n RecordsProcessed?: number;\n /** Total records to process */\n TotalRecords?: number;\n /** Entity currently being processed */\n CurrentEntity?: string;\n}\n\n/**\n * Parameters for creating a version label via the server-side VersionHistoryEngine.\n */\nexport interface CreateVersionLabelParams {\n /** Human-readable name */\n Name: string;\n /** Optional longer description */\n Description?: string;\n /** Scope of the label: System, Entity, or Record (default: Record) */\n Scope?: 'System' | 'Entity' | 'Record';\n /** The target entity name (required for Entity/Record scope) */\n EntityName?: string;\n /** The record's primary key pairs (required for Record scope). Key = field name, Value = field value. */\n RecordKeys?: Array<{ Key: string; Value: string }>;\n /** Optional parent label ID for grouping */\n ParentID?: string;\n /** Optional external system reference */\n ExternalSystemID?: string;\n /** Whether to include dependent records when scope is Record (default: true) */\n IncludeDependencies?: boolean;\n /** Maximum depth of dependency graph traversal */\n MaxDepth?: number;\n /** Entity names to exclude from dependency traversal */\n ExcludeEntities?: string[];\n /**\n * Optional callback invoked with progress updates during label creation.\n * Requires an active PushStatusUpdates subscription (handled automatically).\n */\n OnProgress?: (progress: CreateVersionLabelProgress) => void;\n}\n\n/**\n * Result returned from the CreateVersionLabel mutation.\n */\nexport interface CreateVersionLabelResult {\n Success: boolean;\n LabelID?: string;\n LabelName?: string;\n ItemsCaptured?: number;\n SyntheticSnapshotsCreated?: number;\n Error?: string;\n CaptureErrors?: Array<{\n EntityName: string;\n RecordID: string;\n ErrorMessage: string;\n }>;\n}\n\n// =========================================================================\n// GraphQL Client\n// =========================================================================\n\n/**\n * Client for executing Version History operations through GraphQL.\n *\n * This class provides an easy way to create version labels with proper\n * server-side snapshot capture from a client application.\n *\n * @example\n * ```typescript\n * const vhClient = new GraphQLVersionHistoryClient(graphQLProvider);\n *\n * const result = await vhClient.CreateLabel({\n * Name: 'Before Refactor',\n * Scope: 'Record',\n * EntityName: 'MJ: AI Prompts',\n * RecordKeys: [{ Key: 'ID', Value: recordId }],\n * IncludeDependencies: true,\n * });\n *\n * if (result.Success) {\n * console.log(`Created label ${result.LabelID} with ${result.ItemsCaptured} items`);\n * }\n * ```\n */\nexport class GraphQLVersionHistoryClient {\n private _dataProvider: GraphQLDataProvider;\n\n constructor(dataProvider: GraphQLDataProvider) {\n this._dataProvider = dataProvider;\n }\n\n /**\n * Create a version label with full server-side snapshot capture.\n *\n * This invokes the VersionHistoryEngine on the server which:\n * 1. Creates the VersionLabel record\n * 2. Captures snapshots (VersionLabelItems) based on scope\n * 3. Updates the label with item count and duration metrics\n *\n * If `params.OnProgress` is provided, subscribes to PushStatusUpdates\n * for real-time progress during the operation.\n */\n public async CreateLabel(params: CreateVersionLabelParams): Promise<CreateVersionLabelResult> {\n let subscription: { unsubscribe: () => void } | undefined;\n\n try {\n // Subscribe to progress updates if callback provided\n if (params.OnProgress) {\n subscription = this._dataProvider.PushStatusUpdates(this._dataProvider.sessionId)\n .subscribe((message: string) => {\n try {\n const parsed = JSON.parse(message);\n if (parsed.resolver === 'VersionHistoryResolver' &&\n parsed.type === 'CreateLabelProgress' &&\n parsed.status === 'ok' &&\n parsed.data) {\n params.OnProgress!(parsed.data as CreateVersionLabelProgress);\n }\n } catch (_e) {\n // Ignore parse errors on progress messages\n }\n });\n }\n\n const mutation = gql`\n mutation CreateVersionLabel($input: CreateVersionLabelInput!, $sessionId: String) {\n CreateVersionLabel(input: $input, sessionId: $sessionId) {\n Success\n LabelID\n LabelName\n ItemsCaptured\n SyntheticSnapshotsCreated\n Error\n CaptureErrors {\n EntityName\n RecordID\n ErrorMessage\n }\n }\n }\n `;\n\n const variables = {\n input: this.buildInput(params),\n sessionId: this._dataProvider.sessionId,\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n return this.processResult(result);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n LogError(`GraphQLVersionHistoryClient.CreateLabel error: ${msg}`);\n return { Success: false, Error: msg };\n } finally {\n // Always clean up subscription\n if (subscription) {\n subscription.unsubscribe();\n }\n }\n }\n\n private buildInput(params: CreateVersionLabelParams): Record<string, unknown> {\n const input: Record<string, unknown> = {\n Name: params.Name,\n };\n\n if (params.Description != null) input.Description = params.Description;\n if (params.Scope != null) input.Scope = params.Scope;\n if (params.EntityName != null) input.EntityName = params.EntityName;\n if (params.ParentID != null) input.ParentID = params.ParentID;\n if (params.ExternalSystemID != null) input.ExternalSystemID = params.ExternalSystemID;\n if (params.IncludeDependencies != null) input.IncludeDependencies = params.IncludeDependencies;\n if (params.MaxDepth != null) input.MaxDepth = params.MaxDepth;\n if (params.ExcludeEntities != null) input.ExcludeEntities = params.ExcludeEntities;\n\n if (params.RecordKeys && params.RecordKeys.length > 0) {\n input.RecordKeys = params.RecordKeys.map(kv => ({\n Key: kv.Key,\n Value: kv.Value,\n }));\n }\n\n return input;\n }\n\n private processResult(result: Record<string, unknown>): CreateVersionLabelResult {\n const data = result?.CreateVersionLabel as Record<string, unknown> | undefined;\n if (!data) {\n return { Success: false, Error: 'Invalid response from server.' };\n }\n\n const captureErrors = Array.isArray(data.CaptureErrors)\n ? (data.CaptureErrors as Array<Record<string, string>>).map(e => ({\n EntityName: e.EntityName ?? '',\n RecordID: e.RecordID ?? '',\n ErrorMessage: e.ErrorMessage ?? '',\n }))\n : undefined;\n\n return {\n Success: data.Success as boolean,\n LabelID: data.LabelID as string | undefined,\n LabelName: data.LabelName as string | undefined,\n ItemsCaptured: data.ItemsCaptured as number | undefined,\n SyntheticSnapshotsCreated: data.SyntheticSnapshotsCreated as number | undefined,\n Error: data.Error as string | undefined,\n CaptureErrors: captureErrors,\n };\n }\n}\n","import { LogError } from \"@memberjunction/core\";\nimport { GraphQLDataProvider } from \"./graphQLDataProvider\";\nimport { gql } from \"graphql-request\";\n\n/**\n * Client for file storage operations through GraphQL.\n * This class provides an easy way to interact with file storage accounts from a client application.\n *\n * All operations use accountId (FileStorageAccount ID) as the primary identifier,\n * supporting the enterprise model where storage accounts are organizational resources.\n *\n * @example\n * ```typescript\n * // Create the client\n * const storageClient = new GraphQLFileStorageClient(graphQLProvider);\n *\n * // List objects in a directory\n * const objects = await storageClient.ListObjects(accountId, 'documents/');\n *\n * // Create a pre-authenticated upload URL\n * const uploadResult = await storageClient.CreatePreAuthUploadUrl(\n * accountId,\n * 'documents/report.pdf',\n * 'application/pdf'\n * );\n * ```\n */\nexport class GraphQLFileStorageClient {\n /**\n * The GraphQLDataProvider instance used to execute GraphQL requests\n * @private\n */\n private _dataProvider: GraphQLDataProvider;\n\n /**\n * Creates a new GraphQLFileStorageClient instance.\n * @param dataProvider The GraphQL data provider to use for queries\n */\n constructor(dataProvider: GraphQLDataProvider) {\n this._dataProvider = dataProvider;\n }\n\n // =========================================================================\n // Navigation / Directory Operations\n // =========================================================================\n\n /**\n * List objects in a storage account at the specified path.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param prefix The path prefix to list objects from (e.g., 'documents/')\n * @param delimiter Optional delimiter for grouping results (default: '/')\n * @returns A Promise that resolves to a StorageListResult\n *\n * @example\n * ```typescript\n * const result = await storageClient.ListObjects(accountId, 'documents/', '/');\n * console.log('Files:', result.objects.filter(o => !o.isDirectory));\n * console.log('Folders:', result.prefixes);\n * ```\n */\n public async ListObjects(\n accountId: string,\n prefix: string = '',\n delimiter?: string\n ): Promise<StorageListResult> {\n try {\n const query = gql`\n query ListStorageObjects($input: ListStorageObjectsInput!) {\n ListStorageObjects(input: $input) {\n objects {\n name\n path\n fullPath\n size\n contentType\n lastModified\n isDirectory\n etag\n cacheControl\n }\n prefixes\n }\n }\n `;\n\n const variables = {\n input: {\n AccountID: accountId,\n Prefix: prefix,\n Delimiter: delimiter || '/'\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(query, variables);\n\n if (!result?.ListStorageObjects) {\n throw new Error(\"Invalid response from server\");\n }\n\n return {\n objects: result.ListStorageObjects.objects.map((obj: StorageObjectMetadataResponse) => ({\n ...obj,\n lastModified: new Date(obj.lastModified)\n })),\n prefixes: result.ListStorageObjects.prefixes\n };\n } catch (e) {\n const error = e as Error;\n LogError(`Error listing storage objects: ${error}`);\n throw error;\n }\n }\n\n /**\n * Check if a directory exists in the storage account.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param path The directory path to check\n * @returns A Promise that resolves to true if the directory exists\n */\n public async DirectoryExists(\n accountId: string,\n path: string\n ): Promise<boolean> {\n try {\n // Check by listing with the path as prefix and looking for any results\n const result = await this.ListObjects(accountId, path.endsWith('/') ? path : `${path}/`, '/');\n return result.objects.length > 0 || result.prefixes.length > 0;\n } catch (e) {\n const error = e as Error;\n LogError(`Error checking directory existence: ${error}`);\n return false;\n }\n }\n\n /**\n * Create a directory in the storage account.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param path The directory path to create\n * @returns A Promise that resolves to true if the directory was created successfully\n */\n public async CreateDirectory(\n accountId: string,\n path: string\n ): Promise<boolean> {\n try {\n const mutation = gql`\n mutation CreateDirectory($input: CreateDirectoryInput!) {\n CreateDirectory(input: $input)\n }\n `;\n\n const variables = {\n input: {\n AccountID: accountId,\n Path: path\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n return result?.CreateDirectory ?? false;\n } catch (e) {\n const error = e as Error;\n LogError(`Error creating directory: ${error}`);\n return false;\n }\n }\n\n // =========================================================================\n // File Operations\n // =========================================================================\n\n /**\n * Check if an object exists in the storage account.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param objectName The name/path of the object to check\n * @returns A Promise that resolves to true if the object exists\n */\n public async ObjectExists(\n accountId: string,\n objectName: string\n ): Promise<boolean> {\n try {\n // Try to list the exact object\n const parentPath = objectName.substring(0, objectName.lastIndexOf('/') + 1);\n const fileName = objectName.substring(objectName.lastIndexOf('/') + 1);\n\n const result = await this.ListObjects(accountId, parentPath, '/');\n return result.objects.some(obj => obj.name === fileName || obj.fullPath === objectName);\n } catch (e) {\n const error = e as Error;\n LogError(`Error checking object existence: ${error}`);\n return false;\n }\n }\n\n /**\n * Create a pre-authenticated URL for uploading a file.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param objectName The name/path of the object to upload\n * @param contentType Optional content type for the file\n * @returns A Promise that resolves to the upload URL and provider key\n *\n * @example\n * ```typescript\n * const result = await storageClient.CreatePreAuthUploadUrl(\n * accountId,\n * 'documents/report.pdf',\n * 'application/pdf'\n * );\n *\n * // Use the upload URL to upload the file\n * await fetch(result.uploadUrl, {\n * method: 'PUT',\n * body: fileContent,\n * headers: { 'Content-Type': 'application/pdf' }\n * });\n * ```\n */\n public async CreatePreAuthUploadUrl(\n accountId: string,\n objectName: string,\n contentType?: string\n ): Promise<CreatePreAuthUploadUrlResult> {\n try {\n const mutation = gql`\n mutation CreatePreAuthUploadUrl($input: CreatePreAuthUploadUrlInput!) {\n CreatePreAuthUploadUrl(input: $input) {\n UploadUrl\n ProviderKey\n }\n }\n `;\n\n const variables = {\n input: {\n AccountID: accountId,\n ObjectName: objectName,\n ContentType: contentType\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n\n if (!result?.CreatePreAuthUploadUrl) {\n throw new Error(\"Invalid response from server\");\n }\n\n return {\n uploadUrl: result.CreatePreAuthUploadUrl.UploadUrl,\n providerKey: result.CreatePreAuthUploadUrl.ProviderKey\n };\n } catch (e) {\n const error = e as Error;\n LogError(`Error creating pre-auth upload URL: ${error}`);\n throw error;\n }\n }\n\n /**\n * Create a pre-authenticated URL for downloading a file.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param objectName The name/path of the object to download\n * @returns A Promise that resolves to the download URL\n *\n * @example\n * ```typescript\n * const downloadUrl = await storageClient.CreatePreAuthDownloadUrl(\n * accountId,\n * 'documents/report.pdf'\n * );\n *\n * // Use the download URL\n * window.open(downloadUrl, '_blank');\n * ```\n */\n public async CreatePreAuthDownloadUrl(\n accountId: string,\n objectName: string\n ): Promise<string> {\n try {\n const query = gql`\n query CreatePreAuthDownloadUrl($input: CreatePreAuthDownloadUrlInput!) {\n CreatePreAuthDownloadUrl(input: $input)\n }\n `;\n\n const variables = {\n input: {\n AccountID: accountId,\n ObjectName: objectName\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(query, variables);\n\n if (result?.CreatePreAuthDownloadUrl === undefined) {\n throw new Error(\"Invalid response from server\");\n }\n\n return result.CreatePreAuthDownloadUrl;\n } catch (e) {\n const error = e as Error;\n LogError(`Error creating pre-auth download URL: ${error}`);\n throw error;\n }\n }\n\n /**\n * Delete an object from the storage account.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param objectName The name/path of the object to delete\n * @returns A Promise that resolves to true if the object was deleted successfully\n */\n public async DeleteObject(\n accountId: string,\n objectName: string\n ): Promise<boolean> {\n try {\n const mutation = gql`\n mutation DeleteStorageObject($input: DeleteStorageObjectInput!) {\n DeleteStorageObject(input: $input)\n }\n `;\n\n const variables = {\n input: {\n AccountID: accountId,\n ObjectName: objectName\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n return result?.DeleteStorageObject ?? false;\n } catch (e) {\n const error = e as Error;\n LogError(`Error deleting storage object: ${error}`);\n return false;\n }\n }\n\n /**\n * Move/rename an object within the storage account.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param oldName The current name/path of the object\n * @param newName The new name/path for the object\n * @returns A Promise that resolves to true if the object was moved successfully\n */\n public async MoveObject(\n accountId: string,\n oldName: string,\n newName: string\n ): Promise<boolean> {\n try {\n const mutation = gql`\n mutation MoveStorageObject($input: MoveStorageObjectInput!) {\n MoveStorageObject(input: $input)\n }\n `;\n\n const variables = {\n input: {\n AccountID: accountId,\n OldName: oldName,\n NewName: newName\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n return result?.MoveStorageObject ?? false;\n } catch (e) {\n const error = e as Error;\n LogError(`Error moving storage object: ${error}`);\n return false;\n }\n }\n\n /**\n * Copy an object within the storage account.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param sourceName The source name/path of the object\n * @param destinationName The destination name/path for the copy\n * @returns A Promise that resolves to true if the object was copied successfully\n */\n public async CopyObject(\n accountId: string,\n sourceName: string,\n destinationName: string\n ): Promise<boolean> {\n try {\n const mutation = gql`\n mutation CopyStorageObject($input: CopyStorageObjectInput!) {\n CopyStorageObject(input: $input)\n }\n `;\n\n const variables = {\n input: {\n AccountID: accountId,\n SourceName: sourceName,\n DestinationName: destinationName\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n return result?.CopyStorageObject ?? false;\n } catch (e) {\n const error = e as Error;\n LogError(`Error copying storage object: ${error}`);\n return false;\n }\n }\n\n /**\n * Copy an object between two different storage accounts.\n *\n * @param sourceAccountId The ID of the source FileStorageAccount\n * @param destinationAccountId The ID of the destination FileStorageAccount\n * @param sourcePath The source path of the object\n * @param destinationPath The destination path for the copy\n * @returns A Promise that resolves to the copy result\n */\n public async CopyObjectBetweenAccounts(\n sourceAccountId: string,\n destinationAccountId: string,\n sourcePath: string,\n destinationPath: string\n ): Promise<CopyBetweenAccountsResult> {\n try {\n const mutation = gql`\n mutation CopyObjectBetweenAccounts($input: CopyObjectBetweenAccountsInput!) {\n CopyObjectBetweenAccounts(input: $input) {\n success\n message\n bytesTransferred\n sourceAccount\n destinationAccount\n sourcePath\n destinationPath\n }\n }\n `;\n\n const variables = {\n input: {\n SourceAccountID: sourceAccountId,\n DestinationAccountID: destinationAccountId,\n SourcePath: sourcePath,\n DestinationPath: destinationPath\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n\n if (!result?.CopyObjectBetweenAccounts) {\n throw new Error(\"Invalid response from server\");\n }\n\n return {\n success: result.CopyObjectBetweenAccounts.success,\n message: result.CopyObjectBetweenAccounts.message,\n bytesTransferred: result.CopyObjectBetweenAccounts.bytesTransferred,\n sourceAccount: result.CopyObjectBetweenAccounts.sourceAccount,\n destinationAccount: result.CopyObjectBetweenAccounts.destinationAccount,\n sourcePath: result.CopyObjectBetweenAccounts.sourcePath,\n destinationPath: result.CopyObjectBetweenAccounts.destinationPath\n };\n } catch (e) {\n const error = e as Error;\n LogError(`Error copying object between accounts: ${error}`);\n return {\n success: false,\n message: error.message,\n sourceAccount: '',\n destinationAccount: '',\n sourcePath,\n destinationPath\n };\n }\n }\n\n // =========================================================================\n // Search Operations\n // =========================================================================\n\n /**\n * Search for files across one or more storage accounts.\n *\n * @param accountIds Array of FileStorageAccount IDs to search\n * @param query The search query\n * @param options Optional search options\n * @returns A Promise that resolves to the search results\n *\n * @example\n * ```typescript\n * const results = await storageClient.SearchFiles(\n * [accountId1, accountId2],\n * 'quarterly report',\n * {\n * maxResultsPerAccount: 10,\n * fileTypes: ['pdf', 'docx'],\n * searchContent: true\n * }\n * );\n *\n * for (const accountResult of results.accountResults) {\n * console.log(`Results from ${accountResult.accountName}:`);\n * for (const file of accountResult.results) {\n * console.log(` - ${file.name} (${file.relevance})`);\n * }\n * }\n * ```\n */\n public async SearchFiles(\n accountIds: string[],\n searchQuery: string,\n options?: FileSearchOptions\n ): Promise<SearchAcrossAccountsResult> {\n try {\n const gqlQuery = gql`\n query SearchAcrossAccounts($input: SearchAcrossAccountsInput!) {\n SearchAcrossAccounts(input: $input) {\n accountResults {\n accountID\n accountName\n success\n errorMessage\n results {\n path\n name\n size\n contentType\n lastModified\n relevance\n excerpt\n matchInFilename\n objectId\n }\n totalMatches\n hasMore\n nextPageToken\n }\n totalResultsReturned\n successfulAccounts\n failedAccounts\n }\n }\n `;\n\n const variables = {\n input: {\n AccountIDs: accountIds,\n Query: searchQuery,\n MaxResultsPerAccount: options?.maxResultsPerAccount,\n FileTypes: options?.fileTypes,\n SearchContent: options?.searchContent\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(gqlQuery, variables);\n\n if (!result?.SearchAcrossAccounts) {\n throw new Error(\"Invalid response from server\");\n }\n\n const searchResult = result.SearchAcrossAccounts;\n\n return {\n accountResults: searchResult.accountResults.map((ar: AccountSearchResultResponse) => ({\n accountId: ar.accountID,\n accountName: ar.accountName,\n success: ar.success,\n errorMessage: ar.errorMessage,\n results: ar.results.map((r: FileSearchResultResponse) => ({\n path: r.path,\n name: r.name,\n size: r.size,\n contentType: r.contentType,\n lastModified: new Date(r.lastModified),\n relevance: r.relevance,\n excerpt: r.excerpt,\n matchInFilename: r.matchInFilename,\n objectId: r.objectId\n })),\n totalMatches: ar.totalMatches,\n hasMore: ar.hasMore,\n nextPageToken: ar.nextPageToken\n })),\n totalResultsReturned: searchResult.totalResultsReturned,\n successfulAccounts: searchResult.successfulAccounts,\n failedAccounts: searchResult.failedAccounts\n };\n } catch (e) {\n const error = e as Error;\n LogError(`Error searching across accounts: ${error}`);\n return {\n accountResults: [],\n totalResultsReturned: 0,\n successfulAccounts: 0,\n failedAccounts: accountIds.length\n };\n }\n }\n}\n\n// =========================================================================\n// Type Definitions\n// =========================================================================\n\n/**\n * Metadata for a storage object\n */\nexport interface StorageObjectMetadata {\n /** The name of the object (filename) */\n name: string;\n /** The path to the object (directory) */\n path: string;\n /** The full path including name */\n fullPath: string;\n /** Size in bytes */\n size: number;\n /** MIME content type */\n contentType: string;\n /** Last modification date */\n lastModified: Date;\n /** Whether this is a directory */\n isDirectory: boolean;\n /** ETag for caching */\n etag?: string;\n /** Cache control header */\n cacheControl?: string;\n}\n\n/**\n * Response type for storage object metadata (with string date)\n */\ninterface StorageObjectMetadataResponse {\n name: string;\n path: string;\n fullPath: string;\n size: number;\n contentType: string;\n lastModified: string;\n isDirectory: boolean;\n etag?: string;\n cacheControl?: string;\n}\n\n/**\n * Result from listing storage objects\n */\nexport interface StorageListResult {\n /** Array of objects in the directory */\n objects: StorageObjectMetadata[];\n /** Array of subdirectory prefixes */\n prefixes: string[];\n}\n\n/**\n * Result from creating a pre-authenticated upload URL\n */\nexport interface CreatePreAuthUploadUrlResult {\n /** The URL to use for uploading */\n uploadUrl: string;\n /** The provider-specific key for the object */\n providerKey?: string;\n}\n\n/**\n * Result from copying an object between accounts\n */\nexport interface CopyBetweenAccountsResult {\n /** Whether the copy was successful */\n success: boolean;\n /** Human-readable message */\n message: string;\n /** Number of bytes transferred */\n bytesTransferred?: number;\n /** Name of the source account */\n sourceAccount: string;\n /** Name of the destination account */\n destinationAccount: string;\n /** Source path */\n sourcePath: string;\n /** Destination path */\n destinationPath: string;\n}\n\n/**\n * Options for file search operations\n */\nexport interface FileSearchOptions {\n /** Maximum results per account */\n maxResultsPerAccount?: number;\n /** Filter by file types (extensions) */\n fileTypes?: string[];\n /** Whether to search file content (not just names) */\n searchContent?: boolean;\n}\n\n/**\n * A single file search result\n */\nexport interface FileSearchResult {\n /** Path to the file */\n path: string;\n /** File name */\n name: string;\n /** File size in bytes */\n size: number;\n /** MIME content type */\n contentType: string;\n /** Last modification date */\n lastModified: Date;\n /** Relevance score (0-1) */\n relevance?: number;\n /** Text excerpt showing the match */\n excerpt?: string;\n /** Whether the match was in the filename */\n matchInFilename?: boolean;\n /** Provider-specific object ID */\n objectId?: string;\n}\n\n/**\n * Response type for file search result (with string date)\n */\ninterface FileSearchResultResponse {\n path: string;\n name: string;\n size: number;\n contentType: string;\n lastModified: string;\n relevance?: number;\n excerpt?: string;\n matchInFilename?: boolean;\n objectId?: string;\n}\n\n/**\n * Search results for a single account\n */\nexport interface AccountSearchResult {\n /** The account ID */\n accountId: string;\n /** The account name */\n accountName: string;\n /** Whether the search was successful */\n success: boolean;\n /** Error message if search failed */\n errorMessage?: string;\n /** Search results */\n results: FileSearchResult[];\n /** Total matches found */\n totalMatches?: number;\n /** Whether there are more results */\n hasMore: boolean;\n /** Token for pagination */\n nextPageToken?: string;\n}\n\n/**\n * Response type for account search result (with string dates in results)\n */\ninterface AccountSearchResultResponse {\n accountID: string;\n accountName: string;\n success: boolean;\n errorMessage?: string;\n results: FileSearchResultResponse[];\n totalMatches?: number;\n hasMore: boolean;\n nextPageToken?: string;\n}\n\n/**\n * Result from searching across multiple accounts\n */\nexport interface SearchAcrossAccountsResult {\n /** Results grouped by account */\n accountResults: AccountSearchResult[];\n /** Total results returned across all accounts */\n totalResultsReturned: number;\n /** Number of accounts that were searched successfully */\n successfulAccounts: number;\n /** Number of accounts that failed to search */\n failedAccounts: number;\n}\n"],"names":["FieldMapper","__name","obj","k","fieldName","v","reversed","GraphQLTransactionGroup","TransactionGroupBase","provider","mutation","gql","items","pt","vars","results","data","returnResults","resultJSON","resultObject","SafeJSONParse","item","TransactionResult","GraphQLAIClient","dataProvider","params","variables","result","e","promptResult","parsedResult","validationResult","chatResult","error","LogError","sourceArtifactId","sourceArtifactVersionId","subscription","message","LogStatusEx","parsed","progressWithRunId","embedResult","DEFAULT_CATEGORY","BrowserStorageProviderBase","category","cat","categoryMap","key","value","IDB_DB_NAME","IDB_DB_VERSION","KNOWN_OBJECT_STORES","LEGACY_STORE_NAME","BrowserIndexedDBStorageProvider","openDB","db","storeName","LogErrorEx","storeKey","tx","prefix","store","allKeys","GraphQLProviderConfigData","ProviderConfigDataBase","token","url","wsurl","refreshTokenFunction","MJCoreSchemaName","includeSchemas","excludeSchemas","mjAPIKey","userAPIKey","GraphQLDataProvider","ProviderBase","uuidv4","ls","sessionId","forceRefreshSessionId","oldUUID","configData","providerToUse","separateConnection","d","u","roles","r","UserInfo","contextUser","query","sql","maxRows","timeoutSeconds","input","p","QueryID","CategoryID","CategoryPath","Parameters","MaxRows","StartRow","QueryName","response","transformedResults","deserializedResults","qName","paramType","innerParams","entity","viewEntity","entityName","dynamicView","graphQLTypeName","getGraphQLTypeNameBase","a","fieldList","aggregateResponseFields","viewData","responseAggregates","codeNameDiffFields","f","entityInfos","param","innerParam","index","deserializeData","inputItem","deserializedUpdatedRows","ViewInfo","mapper","kv","c","ProviderType","primaryKey","b","kvps","request","recordID","options","newRequest","fm","user","BaseEntityResult","type","mutationName","filteredFields","inner","outer","i","val","EntityFieldTSType","numValue","ov","mutationInputTypes","TransactionItem","success","EntityRelationshipsToLoad","pkeyInnerParamString","pkeyOuterParamString","field","pkeyGraphQLType","rel","ret","entityInfo","re","uniqueCodeName","returnValues","pk","queryName","returnedVal","originalVal","datasetName","itemFilters","userId","isFavorite","info","CompositeKey","dataContextID","dataContextItemID","refreshTokenIfNeeded","isInstanceSingleton","newToken","newClient","headers","GraphQLClient","UserRoleInfo","object","sOutput","keys","InMemoryLocalStorageProvider","now","createClient","entry","initialCount","entries","toRemove","timeSinceRequested","timeSinceEmission","Observable","observer","client","unsubscribe","errorObj","refreshError","existing","SUBSCRIBE_TO_STATUS","subject","Subject","Subscription","sub","recordPKValue","name","setupGraphQLClient","config","SetProvider","StartupManager","MJGlobal","MJEventType","SyncRolesAndUsersResult","RoleInput","UserInput","RolesAndUsersInput","SyncDataAction","ActionItemInput","SyncDataResult","ActionItemOutput","GraphQLSystemUserClient","queries","accessToken","cleanError","match","IsVerboseLoggingEnabled","verboseError","ID","errorDetails","cause","agentResult","GetDataOutput","SimpleRemoteEntityOutput","SimpleRemoteEntity","SimpleRemoteEntityField","GraphQLActionClient","actionID","skipActionLog","serializedParams","originalParams","resultData","entityObject","kvp","GraphQLEncryptionClient","apiKeyId","GraphQLTestingClient","tagsJson","variablesJson","selectedTestIdsJson","testId","operation","errorMsg","GraphQLComponentRegistryClient","spec","searchResult","json","registryId","componentId","registryName","namespace","GraphQLVersionHistoryClient","msg","captureErrors","GraphQLFileStorageClient","accountId","delimiter","path","objectName","parentPath","fileName","contentType","oldName","newName","sourceName","destinationName","sourceAccountId","destinationAccountId","sourcePath","destinationPath","accountIds","searchQuery","gqlQuery","ar"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/FieldMapper.ts","../src/graphQLTransactionGroup.ts","../src/graphQLAIClient.ts","../src/storage-providers.ts","../src/graphQLDataProvider.ts","../src/config.ts","../src/rolesAndUsersType.ts","../src/graphQLSystemUserClient.ts","../src/graphQLActionClient.ts","../src/graphQLEncryptionClient.ts","../src/graphQLTestingClient.ts","../src/GraphQLComponentRegistryClient.ts","../src/graphQLVersionHistoryClient.ts","../src/graphQLFileStorageClient.ts"],"sourcesContent":["import { RegisterClass } from '@memberjunction/global';\n\n/**\n * FieldMapper is used to map fields from one name to another. This is useful when we need to map\n * fields from one system to another, or when we need to map fields from one version of a system\n * to another. Uses an internal field mapping but may be overridden or extended as needed.\n */\nexport class FieldMapper {\n private _fieldMap: Record<string, string> = {\n __mj_CreatedAt: '_mj__CreatedAt',\n __mj_UpdatedAt: '_mj__UpdatedAt',\n __mj_DeletedAt: '_mj__DeletedAt',\n };\n\n /**\n * Creates a new FieldMapper instance.\n * @param fieldMap An optional field map to use for mapping fields. If not provided, the default field map will be used.\n */\n constructor() {}\n\n /**\n * Maps fields from one name to another mutating the object in place.\n * @param obj The object to mutate\n */\n public MapFields(obj?: Record<string, unknown>) {\n if (obj) {\n for (const k in obj) {\n if (k in this._fieldMap) {\n obj[this._fieldMap[k]] = obj[k];\n delete obj[k];\n }\n }\n }\n return obj;\n }\n\n /**\n * Maps a field name from one name to another.\n * @param fieldName The field name to map.\n * @returns The mapped field name, or the original field name if no mapping is found.\n */\n public MapFieldName(fieldName: string): string {\n return this._fieldMap[fieldName] ?? fieldName;\n }\n\n /**\n * Maps a field name from one name to another using the reverse mapping.\n * @param fieldName The field name to map.\n * @returns The mapped field name, or the original field name if no mapping is found.\n */\n public ReverseMapFieldName(fieldName: string): string {\n return Object.entries(this._fieldMap).find(([k, v]) => v === fieldName)?.[0] ?? fieldName;\n }\n\n /**\n * Maps fields from one name to another mutating the object in place using the reverse mapping.\n * @param obj The object to mutate\n */\n public ReverseMapFields(obj: Record<string, unknown>) {\n const reversed = Object.fromEntries(Object.entries(this._fieldMap).map(([k, v]) => [v, k]));\n for (const k in obj) {\n if (k in reversed) {\n obj[reversed[k]] = obj[k];\n delete obj[k];\n }\n }\n return obj;\n }\n}\n","import { TransactionGroupBase, TransactionResult } from \"@memberjunction/core\";\nimport { GraphQLDataProvider } from \"./graphQLDataProvider\";\nimport { gql } from \"graphql-request\";\nimport { SafeJSONParse } from \"@memberjunction/global\";\n\nexport class GraphQLTransactionGroup extends TransactionGroupBase {\n private _provider: GraphQLDataProvider;\n constructor(provider: GraphQLDataProvider) {\n super();\n this._provider = provider;\n }\n\n // protected async HandleSubmit(): Promise<TransactionResult[]> {\n // // iterate through each instruction and build up the combined query string\n // // and the combined variables object\n // let combinedQuery = '';\n // let mutationParams = '';\n // const combinedVars: any = {};\n\n // for (let i = 0; i < this.PendingTransactions.length; i++) {\n // const item = this.PendingTransactions[i];\n // let itemMutation = item.Instruction;\n // if (item.Vars) {\n // const keys = Object.keys(item.Vars);\n // // rename the variables to avoid collisions and aggregate the varisables\n // // from the item into our combined variables\n // for (let j = 0; j < keys.length; j++) {\n // const key = keys[j];\n // const newKey = `${key}_${i}`;\n // combinedVars[newKey] = item.Vars[key];\n\n // const keyRegEx = new RegExp('\\\\$' + key, 'g'); // Create the RegExp dynamically with the global flag.\n // itemMutation = itemMutation.replace(keyRegEx, '$' + newKey);\n // const mutationInputType = item.ExtraData.mutationInputTypes.find((t: any) => t.varName === key)?.inputType;\n // //{varName: pk.CodeName, inputType: pk.EntityFieldInfo.GraphQLType + '!'}\n // mutationParams += `$${newKey}: ${mutationInputType} \\n`;\n // }\n // }\n // // add in the specific mutation and give it an alias so we can easily figure out the results\n // // from each of them and pass back properly\n // combinedQuery += `mutation_${i}: ` + itemMutation + '\\n';\n // }\n\n // combinedQuery = `mutation TransactionGroup(${mutationParams}){ \\n` + combinedQuery+ '\\n}'; // wrap it up in a mutation so we can execute it\n // const execResults = await this._provider.ExecuteGQL(combinedQuery, combinedVars)\n // const returnResults: TransactionResult[] = [];\n // for (let i = 0; i < this.PendingTransactions.length; i++) {\n // /// NEED TO TEST TO SEE WHAT ORDER WE GET RESULTS BACK AS\n // const result = execResults[`mutation_${i}`];\n // const item = this.PendingTransactions[i];\n // returnResults.push(new TransactionResult(item, result, result !== null));\n // }\n // return returnResults;\n // }\n\n // new implementation\n protected async HandleSubmit(): Promise<TransactionResult[]> {\n // Define the mutation\n const mutation = gql`\n mutation ExecuteTransactionGroup($group: TransactionInputType!) {\n ExecuteTransactionGroup(group: $group) {\n Success\n ErrorMessages\n ResultsJSON\n }\n }\n `;\n\n // Example variables for the mutation\n const items = [];\n for (const pt of this.PendingTransactions) {\n items.push({\n EntityName: pt.BaseEntity.EntityInfo.Name,\n EntityObjectJSON: await pt.BaseEntity.GetDataObjectJSON(),\n OperationType: pt.OperationType\n });\n }\n const vars = {\n group: {\n Items: items,\n Variables: this.Variables.map(v => {\n return {\n Name: v.Name,\n ItemIndex: this.MapVariableEntityObjectToPosition(v),\n FieldName: v.FieldName,\n Type: v.Type\n }\n }) \n }\n }; \n\n const results = await this._provider.ExecuteGQL(mutation, vars)\n if (results && results.ExecuteTransactionGroup) {\n const data = results.ExecuteTransactionGroup;\n const returnResults: TransactionResult[] = [];\n for (let i = 0; i < this.PendingTransactions.length; i++) {\n const resultJSON = data.ResultsJSON[i];\n const resultObject = SafeJSONParse(resultJSON);\n const item = this.PendingTransactions[i];\n returnResults.push(new TransactionResult(item, resultObject, resultObject !== null));\n }\n return returnResults;\n }\n else {\n throw new Error('Failed to execute transaction group');\n }\n }\n}","import { LogError, LogStatusEx } from \"@memberjunction/core\";\nimport { GraphQLDataProvider } from \"./graphQLDataProvider\";\nimport { gql } from \"graphql-request\";\nimport { ExecuteAgentParams, ExecuteAgentResult } from \"@memberjunction/ai-core-plus\";\nimport { SafeJSONParse } from \"@memberjunction/global\";\n\n/**\n * Client for executing AI operations through GraphQL.\n * This class provides an easy way to execute AI prompts and agents from a client application.\n * \n * The GraphQLAIClient follows the same naming convention as other GraphQL clients\n * in the MemberJunction ecosystem, such as GraphQLActionClient and GraphQLSystemUserClient.\n * \n * @example\n * ```typescript\n * // Create the client\n * const aiClient = new GraphQLAIClient(graphQLProvider);\n * \n * // Run an AI prompt\n * const promptResult = await aiClient.RunAIPrompt({\n * promptId: \"prompt-id\",\n * data: { context: \"user data\" },\n * temperature: 0.7\n * });\n * \n * // Run an AI agent\n * const agentResult = await aiClient.RunAIAgent({\n * agentId: \"agent-id\",\n * messages: [{ role: \"user\", content: \"Hello\" }],\n * sessionId: \"session-123\"\n * });\n * ```\n */\nexport class GraphQLAIClient {\n /**\n * Timeout for fire-and-forget agent execution completion wait (15 minutes).\n * Agent execution can take a long time, especially for complex Skip operations.\n * This is a safety net — if no completion event arrives within this window,\n * the client stops waiting and advises the user to refresh.\n */\n private static readonly FIRE_AND_FORGET_TIMEOUT_MS = 15 * 60 * 1000;\n\n /**\n * The GraphQLDataProvider instance used to execute GraphQL requests\n * @private\n */\n private _dataProvider: GraphQLDataProvider;\n\n /**\n * Creates a new GraphQLAIClient instance.\n * @param dataProvider The GraphQL data provider to use for queries\n */\n constructor(dataProvider: GraphQLDataProvider) {\n this._dataProvider = dataProvider;\n }\n\n /**\n * Run an AI prompt with the specified parameters.\n * \n * This method invokes an AI prompt on the server through GraphQL and returns the result.\n * Parameters are automatically serialized as needed, and results are parsed for consumption.\n * \n * @param params The parameters for running the AI prompt\n * @returns A Promise that resolves to a RunAIPromptResult object\n * \n * @example\n * ```typescript\n * const result = await aiClient.RunAIPrompt({\n * promptId: \"prompt-id\",\n * data: { key: \"value\" },\n * temperature: 0.7,\n * topP: 0.9\n * });\n * \n * if (result.success) {\n * console.log('Output:', result.output);\n * console.log('Parsed Result:', result.parsedResult);\n * } else {\n * console.error('Error:', result.error);\n * }\n * ```\n */\n public async RunAIPrompt(params: RunAIPromptParams): Promise<RunAIPromptResult> {\n try {\n // Build the mutation with all possible parameters\n const mutation = gql`\n mutation RunAIPrompt(\n $promptId: String!,\n $data: String,\n $overrideModelId: String,\n $overrideVendorId: String,\n $configurationId: String,\n $skipValidation: Boolean,\n $templateData: String,\n $responseFormat: String,\n $temperature: Float,\n $topP: Float,\n $topK: Int,\n $minP: Float,\n $frequencyPenalty: Float,\n $presencePenalty: Float,\n $seed: Int,\n $stopSequences: [String!],\n $includeLogProbs: Boolean,\n $topLogProbs: Int,\n $messages: String,\n $rerunFromPromptRunID: String,\n $systemPromptOverride: String\n ) {\n RunAIPrompt(\n promptId: $promptId,\n data: $data,\n overrideModelId: $overrideModelId,\n overrideVendorId: $overrideVendorId,\n configurationId: $configurationId,\n skipValidation: $skipValidation,\n templateData: $templateData,\n responseFormat: $responseFormat,\n temperature: $temperature,\n topP: $topP,\n topK: $topK,\n minP: $minP,\n frequencyPenalty: $frequencyPenalty,\n presencePenalty: $presencePenalty,\n seed: $seed,\n stopSequences: $stopSequences,\n includeLogProbs: $includeLogProbs,\n topLogProbs: $topLogProbs,\n messages: $messages,\n rerunFromPromptRunID: $rerunFromPromptRunID,\n systemPromptOverride: $systemPromptOverride\n ) {\n success\n output\n parsedResult\n error\n executionTimeMs\n tokensUsed\n promptRunId\n rawResult\n validationResult\n chatResult\n }\n }\n `;\n\n // Prepare variables, serializing complex objects to JSON strings\n const variables = this.preparePromptVariables(params);\n\n // Execute the mutation\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n\n // Process and return the result\n return this.processPromptResult(result);\n } catch (e) {\n return this.handlePromptError(e);\n }\n }\n\n /**\n * Prepares variables for the AI prompt mutation\n * @param params The prompt parameters\n * @returns The prepared variables for GraphQL\n * @private\n */\n private preparePromptVariables(params: RunAIPromptParams): Record<string, any> {\n const variables: Record<string, any> = {\n promptId: params.promptId\n };\n\n // Serialize complex objects to JSON strings\n if (params.data !== undefined) {\n variables.data = typeof params.data === 'object' ? JSON.stringify(params.data) : params.data;\n }\n if (params.templateData !== undefined) {\n variables.templateData = typeof params.templateData === 'object' ? JSON.stringify(params.templateData) : params.templateData;\n }\n if (params.messages !== undefined) {\n variables.messages = JSON.stringify(params.messages);\n }\n\n // Add optional scalar parameters\n if (params.overrideModelId !== undefined) variables.overrideModelId = params.overrideModelId;\n if (params.overrideVendorId !== undefined) variables.overrideVendorId = params.overrideVendorId;\n if (params.configurationId !== undefined) variables.configurationId = params.configurationId;\n if (params.skipValidation !== undefined) variables.skipValidation = params.skipValidation;\n if (params.responseFormat !== undefined) variables.responseFormat = params.responseFormat;\n if (params.temperature !== undefined) variables.temperature = params.temperature;\n if (params.topP !== undefined) variables.topP = params.topP;\n if (params.topK !== undefined) variables.topK = params.topK;\n if (params.minP !== undefined) variables.minP = params.minP;\n if (params.frequencyPenalty !== undefined) variables.frequencyPenalty = params.frequencyPenalty;\n if (params.presencePenalty !== undefined) variables.presencePenalty = params.presencePenalty;\n if (params.seed !== undefined) variables.seed = params.seed;\n if (params.stopSequences !== undefined) variables.stopSequences = params.stopSequences;\n if (params.includeLogProbs !== undefined) variables.includeLogProbs = params.includeLogProbs;\n if (params.topLogProbs !== undefined) variables.topLogProbs = params.topLogProbs;\n if (params.rerunFromPromptRunID !== undefined) variables.rerunFromPromptRunID = params.rerunFromPromptRunID;\n if (params.systemPromptOverride !== undefined) variables.systemPromptOverride = params.systemPromptOverride;\n\n return variables;\n }\n\n /**\n * Processes the result from the AI prompt mutation\n * @param result The raw GraphQL result\n * @returns The processed RunAIPromptResult\n * @private\n */\n private processPromptResult(result: any): RunAIPromptResult {\n if (!result?.RunAIPrompt) {\n throw new Error(\"Invalid response from server\");\n }\n\n const promptResult = result.RunAIPrompt;\n\n // Parse JSON results if they exist\n let parsedResult: any;\n let validationResult: any;\n let chatResult: any;\n\n try {\n if (promptResult.parsedResult) {\n parsedResult = JSON.parse(promptResult.parsedResult);\n }\n } catch (e) {\n // Keep as string if parsing fails\n parsedResult = promptResult.parsedResult;\n }\n\n try {\n if (promptResult.validationResult) {\n validationResult = JSON.parse(promptResult.validationResult);\n }\n } catch (e) {\n validationResult = promptResult.validationResult;\n }\n\n try {\n if (promptResult.chatResult) {\n chatResult = JSON.parse(promptResult.chatResult);\n }\n } catch (e) {\n chatResult = promptResult.chatResult;\n }\n\n return {\n success: promptResult.success,\n output: promptResult.output,\n parsedResult,\n error: promptResult.error,\n executionTimeMs: promptResult.executionTimeMs,\n tokensUsed: promptResult.tokensUsed,\n promptRunId: promptResult.promptRunId,\n rawResult: promptResult.rawResult,\n validationResult,\n chatResult\n };\n }\n\n /**\n * Handles errors in the AI prompt execution\n * @param e The error\n * @returns An error result\n * @private\n */\n private handlePromptError(e: unknown): RunAIPromptResult {\n const error = e as Error;\n LogError(`Error running AI prompt: ${error}`);\n return {\n success: false,\n error: error.message || 'Unknown error occurred'\n };\n }\n\n /**\n * Run an AI agent with the specified parameters.\n *\n * This method invokes an AI agent on the server through GraphQL and returns the result.\n * The agent can maintain conversation context across multiple interactions.\n *\n * If a progress callback is provided in params.onProgress, this method will subscribe\n * to real-time progress updates from the GraphQL server and forward them to the callback.\n *\n * @param params The parameters for running the AI agent\n * @returns A Promise that resolves to a RunAIAgentResult object\n *\n * @example\n * ```typescript\n * const result = await aiClient.RunAIAgent({\n * agentId: \"agent-id\",\n * messages: [\n * { role: \"user\", content: \"What's the weather like?\" }\n * ],\n * sessionId: \"session-123\",\n * data: { location: \"New York\" },\n * onProgress: (progress) => {\n * console.log(`Progress: ${progress.message} (${progress.percentage}%)`);\n * }\n * });\n *\n * if (result.success) {\n * console.log('Response:', result.payload);\n * console.log('Execution time:', result.executionTimeMs, 'ms');\n * } else {\n * console.error('Error:', result.errorMessage);\n * }\n * ```\n */\n public async RunAIAgent(\n params: ExecuteAgentParams,\n sourceArtifactId?: string,\n sourceArtifactVersionId?: string\n ): Promise<ExecuteAgentResult> {\n let subscription: any;\n\n try {\n // Subscribe to progress updates if callback provided\n if (params.onProgress) {\n subscription = this._dataProvider.PushStatusUpdates(this._dataProvider.sessionId)\n .subscribe((message: string) => {\n try {\n LogStatusEx({message: '[GraphQLAIClient] Received statusUpdate message', verboseOnly: true, additionalArgs: [message]});\n const parsed = JSON.parse(message);\n LogStatusEx({message: '[GraphQLAIClient] Parsed message', verboseOnly: true, additionalArgs: [parsed]});\n\n // Filter for ExecutionProgress messages from RunAIAgentResolver\n if (parsed.resolver === 'RunAIAgentResolver' &&\n parsed.type === 'ExecutionProgress' &&\n parsed.status === 'ok' &&\n parsed.data?.progress) {\n\n LogStatusEx({message: '[GraphQLAIClient] Forwarding progress to callback', verboseOnly: true, additionalArgs: [parsed.data.progress]});\n // Forward progress to callback with agentRunId in metadata\n const progressWithRunId = {\n ...parsed.data.progress,\n metadata: {\n ...(parsed.data.progress.metadata || {}),\n agentRunId: parsed.data.agentRunId\n }\n };\n params.onProgress!(progressWithRunId);\n } else {\n LogStatusEx({message: '[GraphQLAIClient] Message does not match filter criteria', verboseOnly: true, additionalArgs: [{\n resolver: parsed.resolver,\n type: parsed.type,\n status: parsed.status,\n hasProgress: !!parsed.data?.progress\n }]});\n }\n } catch (e) {\n // Log parsing errors for debugging\n console.error('[GraphQLAIClient] Failed to parse progress message:', e, 'Raw message:', message);\n }\n });\n }\n\n // Build the mutation\n const mutation = gql`\n mutation RunAIAgent(\n $agentId: String!,\n $messages: String!,\n $sessionId: String!,\n $data: String,\n $payload: String,\n $templateData: String,\n $lastRunId: String,\n $autoPopulateLastRunPayload: Boolean,\n $configurationId: String,\n $conversationDetailId: String,\n $createArtifacts: Boolean,\n $createNotification: Boolean,\n $sourceArtifactId: String,\n $sourceArtifactVersionId: String\n ) {\n RunAIAgent(\n agentId: $agentId,\n messages: $messages,\n sessionId: $sessionId,\n data: $data,\n payload: $payload,\n templateData: $templateData,\n lastRunId: $lastRunId,\n autoPopulateLastRunPayload: $autoPopulateLastRunPayload,\n configurationId: $configurationId,\n conversationDetailId: $conversationDetailId,\n createArtifacts: $createArtifacts,\n createNotification: $createNotification,\n sourceArtifactId: $sourceArtifactId,\n sourceArtifactVersionId: $sourceArtifactVersionId\n ) {\n success\n errorMessage\n executionTimeMs\n result\n }\n }\n `;\n\n // Prepare variables\n const variables = this.prepareAgentVariables(params, sourceArtifactId, sourceArtifactVersionId);\n\n // Execute the mutation\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n\n // Process and return the result\n return this.processAgentResult(result.RunAIAgent?.result);\n } catch (e) {\n return this.handleAgentError(e);\n } finally {\n // Always clean up subscription\n if (subscription) {\n subscription.unsubscribe();\n }\n }\n }\n\n /**\n * Prepares variables for the AI agent mutation\n * @param params The agent parameters\n * @param sourceArtifactId Optional source artifact ID for versioning\n * @param sourceArtifactVersionId Optional source artifact version ID for versioning\n * @returns The prepared variables for GraphQL\n * @private\n */\n private prepareAgentVariables(\n params: ExecuteAgentParams,\n sourceArtifactId?: string,\n sourceArtifactVersionId?: string\n ): Record<string, any> {\n const variables: Record<string, any> = {\n agentId: params.agent.ID,\n messages: JSON.stringify(params.conversationMessages),\n sessionId: this._dataProvider.sessionId\n };\n\n // Serialize optional complex objects to JSON strings\n if (params.data !== undefined) {\n variables.data = typeof params.data === 'object' ? JSON.stringify(params.data) : params.data;\n } \n if (params.payload !== undefined) {\n variables.payload = typeof params.payload === 'object' ? JSON.stringify(params.payload) : params.payload;\n }\n\n // Add optional scalar parameters\n if (params.lastRunId !== undefined) variables.lastRunId = params.lastRunId;\n if (params.autoPopulateLastRunPayload !== undefined) variables.autoPopulateLastRunPayload = params.autoPopulateLastRunPayload;\n if (params.configurationId !== undefined) variables.configurationId = params.configurationId;\n if (params.conversationDetailId !== undefined) {\n variables.conversationDetailId = params.conversationDetailId;\n // When conversationDetailId is provided, enable server-side artifact and notification creation\n // This is a GraphQL resolver-level concern, not agent execution concern\n variables.createArtifacts = true;\n variables.createNotification = true;\n }\n // Add source artifact tracking for versioning (GraphQL resolver-level concern)\n if (sourceArtifactId !== undefined) variables.sourceArtifactId = sourceArtifactId;\n if (sourceArtifactVersionId !== undefined) variables.sourceArtifactVersionId = sourceArtifactVersionId;\n\n return variables;\n }\n\n /**\n * Processes the result from the AI agent mutation\n * @param result The raw GraphQL result\n * @returns The processed RunAIAgentResult\n * @private\n */\n private processAgentResult(result: string): ExecuteAgentResult {\n return SafeJSONParse(result) as ExecuteAgentResult; \n }\n\n /**\n * Handles errors in the AI agent execution\n * @param e The error\n * @returns An error result\n * @private\n */\n private handleAgentError(e: unknown): ExecuteAgentResult {\n const error = e as Error;\n const errorMessage = error?.message || String(e);\n LogError(`Error running AI agent: ${errorMessage}`);\n\n // Provide a meaningful error message that helps the user understand what happened.\n // CORS/network errors from Azure proxy timeouts appear as \"Failed to fetch\".\n const isFetchError = errorMessage.includes('Failed to fetch') || errorMessage.includes('NetworkError');\n const isTimeoutError = errorMessage.includes('timed out') || errorMessage.includes('timeout');\n\n let userMessage: string;\n if (isFetchError) {\n userMessage = 'Lost connection to the server. The agent may still be running. Please refresh to check the latest status.';\n } else if (isTimeoutError) {\n userMessage = errorMessage; // Already has a helpful message from the completion timeout\n } else {\n userMessage = errorMessage;\n }\n\n return {\n success: false,\n agentRun: undefined,\n errorMessage: userMessage\n } as ExecuteAgentResult;\n }\n\n /**\n * Run an AI agent using an existing conversation detail ID.\n * This is an optimized method that loads conversation history server-side,\n * avoiding the need to send large attachment data from client to server.\n *\n * Use this method when:\n * - The user's message has already been saved as a ConversationDetail\n * - The conversation may have attachments (images, documents, etc.)\n * - You want optimal performance by loading history on the server\n *\n * @param params The parameters for running the AI agent from conversation detail\n * @returns A Promise that resolves to an ExecuteAgentResult object\n *\n * @example\n * ```typescript\n * const result = await aiClient.RunAIAgentFromConversationDetail({\n * conversationDetailId: \"detail-id-123\",\n * agentId: \"agent-id\",\n * maxHistoryMessages: 20,\n * createArtifacts: true,\n * onProgress: (progress) => {\n * console.log(`Progress: ${progress.message}`);\n * }\n * });\n * ```\n */\n public async RunAIAgentFromConversationDetail(\n params: RunAIAgentFromConversationDetailParams\n ): Promise<ExecuteAgentResult> {\n let subscription: ReturnType<typeof this._dataProvider.PushStatusUpdates.prototype.subscribe> | undefined;\n let completionTimeoutId: ReturnType<typeof setTimeout> | undefined;\n\n try {\n // Set up a promise that resolves when we receive the completion event via WebSocket.\n // This allows us to use fire-and-forget mode where the HTTP mutation returns immediately,\n // avoiding Azure Web App proxy timeouts (~230s) on long-running agent executions.\n let resolveCompletion!: (value: ExecuteAgentResult) => void;\n let rejectCompletion!: (reason: Error) => void;\n const completionPromise = new Promise<ExecuteAgentResult>((resolve, reject) => {\n resolveCompletion = resolve;\n rejectCompletion = reject;\n });\n\n // Set up timeout for the completion wait\n completionTimeoutId = setTimeout(() => {\n rejectCompletion(new Error(\n 'The agent may still be running on the server but the connection timed out after 15 minutes. ' +\n 'Please refresh the page to check the latest status.'\n ));\n }, GraphQLAIClient.FIRE_AND_FORGET_TIMEOUT_MS);\n\n // Always subscribe to PubSub updates (for both progress forwarding and completion detection)\n subscription = this._dataProvider.PushStatusUpdates(this._dataProvider.sessionId)\n .subscribe((message: string) => {\n try {\n const parsed = JSON.parse(message);\n\n // Forward progress updates to callback if provided\n if (params.onProgress &&\n parsed.resolver === 'RunAIAgentResolver' &&\n parsed.type === 'ExecutionProgress' &&\n parsed.status === 'ok' &&\n parsed.data?.progress) {\n\n // Forward progress to callback with agentRunId in metadata\n const progressWithRunId = {\n ...parsed.data.progress,\n metadata: {\n ...(parsed.data.progress.metadata || {}),\n agentRunId: parsed.data.agentRunId\n }\n };\n params.onProgress!(progressWithRunId);\n }\n\n // Listen for completion event matching our conversationDetailId.\n // The server publishes this via PublishStreamingUpdate with type='StreamingContent'\n // and the inner data has type='complete'.\n if (parsed.resolver === 'RunAIAgentResolver' &&\n parsed.type === 'StreamingContent' &&\n parsed.status === 'ok' &&\n parsed.data?.type === 'complete' &&\n parsed.data?.conversationDetailId === params.conversationDetailId) {\n\n if (completionTimeoutId) clearTimeout(completionTimeoutId);\n\n // Parse the enriched result data included in the completion event\n if (parsed.data.result) {\n resolveCompletion(SafeJSONParse(parsed.data.result) as ExecuteAgentResult);\n } else {\n // Completion event without result data (backward compatibility)\n resolveCompletion({\n success: parsed.data.success !== false,\n agentRun: undefined\n });\n }\n }\n } catch (e) {\n console.error('[GraphQLAIClient] Failed to parse progress message:', e);\n }\n });\n\n // Build the fire-and-forget mutation\n const mutation = gql`\n mutation RunAIAgentFromConversationDetail(\n $conversationDetailId: String!,\n $agentId: String!,\n $sessionId: String!,\n $maxHistoryMessages: Int,\n $data: String,\n $payload: String,\n $lastRunId: String,\n $autoPopulateLastRunPayload: Boolean,\n $configurationId: String,\n $createArtifacts: Boolean,\n $createNotification: Boolean,\n $sourceArtifactId: String,\n $sourceArtifactVersionId: String,\n $fireAndForget: Boolean\n ) {\n RunAIAgentFromConversationDetail(\n conversationDetailId: $conversationDetailId,\n agentId: $agentId,\n sessionId: $sessionId,\n maxHistoryMessages: $maxHistoryMessages,\n data: $data,\n payload: $payload,\n lastRunId: $lastRunId,\n autoPopulateLastRunPayload: $autoPopulateLastRunPayload,\n configurationId: $configurationId,\n createArtifacts: $createArtifacts,\n createNotification: $createNotification,\n sourceArtifactId: $sourceArtifactId,\n sourceArtifactVersionId: $sourceArtifactVersionId,\n fireAndForget: $fireAndForget\n ) {\n success\n errorMessage\n executionTimeMs\n result\n }\n }\n `;\n\n // Prepare variables with fireAndForget enabled\n const variables: Record<string, unknown> = {\n conversationDetailId: params.conversationDetailId,\n agentId: params.agentId,\n sessionId: this._dataProvider.sessionId,\n fireAndForget: true\n };\n\n // Add optional parameters\n if (params.maxHistoryMessages !== undefined) variables.maxHistoryMessages = params.maxHistoryMessages;\n if (params.data !== undefined) {\n variables.data = typeof params.data === 'object' ? JSON.stringify(params.data) : params.data;\n }\n if (params.payload !== undefined) {\n variables.payload = typeof params.payload === 'object' ? JSON.stringify(params.payload) : params.payload;\n }\n if (params.lastRunId !== undefined) variables.lastRunId = params.lastRunId;\n if (params.autoPopulateLastRunPayload !== undefined) variables.autoPopulateLastRunPayload = params.autoPopulateLastRunPayload;\n if (params.configurationId !== undefined) variables.configurationId = params.configurationId;\n if (params.createArtifacts !== undefined) variables.createArtifacts = params.createArtifacts;\n if (params.createNotification !== undefined) variables.createNotification = params.createNotification;\n if (params.sourceArtifactId !== undefined) variables.sourceArtifactId = params.sourceArtifactId;\n if (params.sourceArtifactVersionId !== undefined) variables.sourceArtifactVersionId = params.sourceArtifactVersionId;\n\n // Execute the fire-and-forget mutation (returns immediately after server validates input)\n const mutationResult = await this._dataProvider.ExecuteGQL(mutation, variables);\n const ack = mutationResult.RunAIAgentFromConversationDetail;\n\n // Check if the server accepted the request\n if (!ack?.success) {\n if (completionTimeoutId) clearTimeout(completionTimeoutId);\n return {\n success: false,\n agentRun: undefined,\n errorMessage: ack?.errorMessage || 'Server rejected the agent execution request'\n } as ExecuteAgentResult;\n }\n\n // Server accepted - wait for completion via WebSocket\n return await completionPromise;\n } catch (e) {\n if (completionTimeoutId) clearTimeout(completionTimeoutId);\n return this.handleAgentError(e);\n } finally {\n // Always clean up subscription and timeout\n if (completionTimeoutId) clearTimeout(completionTimeoutId);\n if (subscription) {\n subscription.unsubscribe();\n }\n }\n }\n\n /**\n * Execute a simple prompt without requiring a stored AI Prompt entity.\n * This method is designed for interactive components that need quick AI responses.\n * \n * @param params The parameters for the simple prompt execution\n * @returns A Promise that resolves to a SimplePromptResult object\n * \n * @example\n * ```typescript\n * const result = await aiClient.ExecuteSimplePrompt({\n * systemPrompt: \"You are a helpful assistant\",\n * messages: [\n * { message: \"What's the weather?\", role: \"user\" }\n * ],\n * modelPower: \"medium\"\n * });\n * \n * if (result.success) {\n * console.log('Response:', result.result);\n * if (result.resultObject) {\n * console.log('Parsed JSON:', JSON.parse(result.resultObject));\n * }\n * }\n * ```\n */\n public async ExecuteSimplePrompt(params: ExecuteSimplePromptParams): Promise<SimplePromptResult> {\n try {\n const mutation = gql`\n mutation ExecuteSimplePrompt(\n $systemPrompt: String!,\n $messages: String,\n $preferredModels: [String!],\n $modelPower: String,\n $responseFormat: String\n ) {\n ExecuteSimplePrompt(\n systemPrompt: $systemPrompt,\n messages: $messages,\n preferredModels: $preferredModels,\n modelPower: $modelPower,\n responseFormat: $responseFormat\n ) {\n success\n result\n resultObject\n modelName\n error\n executionTimeMs\n }\n }\n `;\n\n // Prepare variables\n const variables: Record<string, any> = {\n systemPrompt: params.systemPrompt\n };\n\n // Convert messages array to JSON string if provided\n if (params.messages && params.messages.length > 0) {\n variables.messages = JSON.stringify(params.messages);\n }\n\n if (params.preferredModels) {\n variables.preferredModels = params.preferredModels;\n }\n\n if (params.modelPower) {\n variables.modelPower = params.modelPower;\n }\n\n if (params.responseFormat) {\n variables.responseFormat = params.responseFormat;\n }\n\n // Execute the mutation\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n\n if (!result?.ExecuteSimplePrompt) {\n throw new Error(\"Invalid response from server\");\n }\n\n const promptResult = result.ExecuteSimplePrompt;\n\n // Parse resultObject if it exists\n let resultObject: any;\n if (promptResult.resultObject) {\n try {\n resultObject = JSON.parse(promptResult.resultObject);\n } catch (e) {\n resultObject = promptResult.resultObject;\n }\n }\n\n return {\n success: promptResult.success,\n result: promptResult.result,\n resultObject,\n modelName: promptResult.modelName,\n error: promptResult.error,\n executionTimeMs: promptResult.executionTimeMs\n };\n\n } catch (e) {\n const error = e as Error;\n LogError(`Error executing simple prompt: ${error}`);\n return {\n success: false,\n modelName: 'Unknown',\n error: error.message || 'Unknown error occurred'\n };\n }\n }\n\n /**\n * Generate embeddings for text using local embedding models.\n * This method is designed for interactive components that need fast similarity calculations.\n * \n * @param params The parameters for embedding generation\n * @returns A Promise that resolves to an EmbedTextResult object\n * \n * @example\n * ```typescript\n * const result = await aiClient.EmbedText({\n * textToEmbed: [\"Hello world\", \"How are you?\"],\n * modelSize: \"small\"\n * });\n * \n * console.log('Embeddings:', result.embeddings);\n * console.log('Model used:', result.modelName);\n * console.log('Dimensions:', result.vectorDimensions);\n * ```\n */\n public async EmbedText(params: EmbedTextParams): Promise<EmbedTextResult> {\n try {\n const mutation = gql`\n mutation EmbedText(\n $textToEmbed: [String!]!,\n $modelSize: String!\n ) {\n EmbedText(\n textToEmbed: $textToEmbed,\n modelSize: $modelSize\n ) {\n embeddings\n modelName\n vectorDimensions\n error\n }\n }\n `;\n\n // Prepare variables - handle both single string and array\n const textArray = Array.isArray(params.textToEmbed) \n ? params.textToEmbed \n : [params.textToEmbed];\n\n const variables = {\n textToEmbed: textArray,\n modelSize: params.modelSize\n };\n\n // Execute the mutation\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n\n if (!result?.EmbedText) {\n throw new Error(\"Invalid response from server\");\n }\n\n const embedResult = result.EmbedText;\n\n // Return single embedding or array based on input\n const returnEmbeddings = Array.isArray(params.textToEmbed)\n ? embedResult.embeddings\n : embedResult.embeddings[0];\n\n return {\n embeddings: returnEmbeddings,\n modelName: embedResult.modelName,\n vectorDimensions: embedResult.vectorDimensions,\n error: embedResult.error\n };\n\n } catch (e) {\n const error = e as Error;\n LogError(`Error generating embeddings: ${error}`);\n return {\n embeddings: Array.isArray(params.textToEmbed) ? [] : [],\n modelName: 'Unknown',\n vectorDimensions: 0,\n error: error.message || 'Unknown error occurred'\n };\n }\n }\n}\n\n/**\n * Parameters for executing a simple prompt\n */\nexport interface ExecuteSimplePromptParams {\n /**\n * The system prompt to set context for the model\n */\n systemPrompt: string;\n \n /**\n * Optional message history\n */\n messages?: Array<{message: string, role: 'user' | 'assistant'}>;\n \n /**\n * Preferred model names to use\n */\n preferredModels?: string[];\n \n /**\n * Power level for model selection\n */\n modelPower?: 'lowest' | 'medium' | 'highest';\n \n /**\n * Response format (e.g., \"json\")\n */\n responseFormat?: string;\n}\n\n/**\n * Result from executing a simple prompt\n */\nexport interface SimplePromptResult {\n /**\n * Whether the execution was successful\n */\n success: boolean;\n \n /**\n * The text response from the model\n */\n result?: string;\n \n /**\n * Parsed JSON object if the response contained JSON\n */\n resultObject?: any;\n \n /**\n * Name of the model used\n */\n modelName: string;\n \n /**\n * Error message if execution failed\n */\n error?: string;\n \n /**\n * Execution time in milliseconds\n */\n executionTimeMs?: number;\n}\n\n/**\n * Parameters for generating text embeddings\n */\nexport interface EmbedTextParams {\n /**\n * Text or array of texts to embed\n */\n textToEmbed: string | string[];\n \n /**\n * Size of the embedding model to use\n */\n modelSize: 'small' | 'medium';\n}\n\n/**\n * Result from generating text embeddings\n */\nexport interface EmbedTextResult {\n /**\n * Single embedding vector or array of vectors\n */\n embeddings: number[] | number[][];\n \n /**\n * Name of the model used\n */\n modelName: string;\n \n /**\n * Number of dimensions in each vector\n */\n vectorDimensions: number;\n \n /**\n * Error message if generation failed\n */\n error?: string;\n}\n\n/**\n * Parameters for running an AI prompt\n */\nexport interface RunAIPromptParams {\n /**\n * The ID of the AI prompt to run\n */\n promptId: string;\n \n /**\n * Data context to pass to the prompt (will be JSON serialized)\n */\n data?: Record<string, any>;\n \n /**\n * Override the default model ID\n */\n overrideModelId?: string;\n \n /**\n * Override the default vendor ID\n */\n overrideVendorId?: string;\n \n /**\n * Configuration ID to use\n */\n configurationId?: string;\n \n /**\n * Skip validation of the prompt\n */\n skipValidation?: boolean;\n \n /**\n * Template data for prompt templating (will be JSON serialized)\n */\n templateData?: Record<string, any>;\n \n /**\n * Response format (e.g., \"json\", \"text\")\n */\n responseFormat?: string;\n \n /**\n * Temperature parameter for the model (0.0 to 1.0)\n */\n temperature?: number;\n \n /**\n * Top-p sampling parameter\n */\n topP?: number;\n \n /**\n * Top-k sampling parameter\n */\n topK?: number;\n \n /**\n * Min-p sampling parameter\n */\n minP?: number;\n \n /**\n * Frequency penalty parameter\n */\n frequencyPenalty?: number;\n \n /**\n * Presence penalty parameter\n */\n presencePenalty?: number;\n \n /**\n * Random seed for reproducible outputs\n */\n seed?: number;\n \n /**\n * Stop sequences for the model\n */\n stopSequences?: string[];\n \n /**\n * Include log probabilities in the response\n */\n includeLogProbs?: boolean;\n \n /**\n * Number of top log probabilities to include\n */\n topLogProbs?: number;\n \n /**\n * Conversation messages (will be JSON serialized)\n */\n messages?: Array<{ role: string; content: string }>;\n \n /**\n * ID of a previous prompt run to rerun from\n */\n rerunFromPromptRunID?: string;\n \n /**\n * Override the system prompt\n */\n systemPromptOverride?: string;\n}\n\n/**\n * Result from running an AI prompt\n */\nexport interface RunAIPromptResult {\n /**\n * Whether the prompt execution was successful\n */\n success: boolean;\n \n /**\n * The output from the prompt\n */\n output?: string;\n \n /**\n * Parsed result data (if applicable)\n */\n parsedResult?: any;\n \n /**\n * Error message if the execution failed\n */\n error?: string;\n \n /**\n * Execution time in milliseconds\n */\n executionTimeMs?: number;\n \n /**\n * Number of tokens used\n */\n tokensUsed?: number;\n \n /**\n * ID of the prompt run record\n */\n promptRunId?: string;\n \n /**\n * Raw result from the model\n */\n rawResult?: string;\n \n /**\n * Validation result data\n */\n validationResult?: any;\n \n /**\n * Chat completion result data\n */\n chatResult?: any;\n}\n\n/**\n * Parameters for running an AI agent from an existing conversation detail.\n * This is the optimized method that loads conversation history server-side.\n */\nexport interface RunAIAgentFromConversationDetailParams {\n /**\n * The ID of the conversation detail (user's message) to use as context\n */\n conversationDetailId: string;\n\n /**\n * The ID of the AI agent to run\n */\n agentId: string;\n\n /**\n * Maximum number of history messages to include (default: 20)\n */\n maxHistoryMessages?: number;\n\n /**\n * Data context to pass to the agent (will be JSON serialized)\n */\n data?: Record<string, unknown>;\n\n /**\n * Payload to pass to the agent (will be JSON serialized)\n */\n payload?: Record<string, unknown> | string;\n\n /**\n * ID of the last agent run for continuity\n */\n lastRunId?: string;\n\n /**\n * Whether to auto-populate payload from last run\n */\n autoPopulateLastRunPayload?: boolean;\n\n /**\n * Configuration ID to use\n */\n configurationId?: string;\n\n /**\n * Whether to create artifacts from the agent's payload\n */\n createArtifacts?: boolean;\n\n /**\n * Whether to create a user notification on completion\n */\n createNotification?: boolean;\n\n /**\n * Source artifact ID for versioning\n */\n sourceArtifactId?: string;\n\n /**\n * Source artifact version ID for versioning\n */\n sourceArtifactVersionId?: string;\n\n /**\n * Optional callback for progress updates\n */\n onProgress?: (progress: {\n currentStep: string;\n percentage?: number;\n message: string;\n metadata?: Record<string, unknown>;\n }) => void;\n}\n","import { ILocalStorageProvider, LogError, LogErrorEx } from '@memberjunction/core';\nimport { openDB, DBSchema, IDBPDatabase } from '@tempfix/idb';\n\n// Default category used when no category is specified\nconst DEFAULT_CATEGORY = 'default';\n\n// ============================================================================\n// IN-MEMORY STORAGE PROVIDER (Map of Maps)\n// ============================================================================\n\n/**\n * In-memory storage provider using nested Map structure for category isolation.\n * Used as a fallback when browser storage is not available.\n *\n * Storage structure: Map<category, Map<key, value>>\n */\nexport class BrowserStorageProviderBase implements ILocalStorageProvider {\n private _storage: Map<string, Map<string, string>> = new Map();\n\n /**\n * Gets or creates a category map\n */\n private getCategoryMap(category: string): Map<string, string> {\n const cat = category || DEFAULT_CATEGORY;\n let categoryMap = this._storage.get(cat);\n if (!categoryMap) {\n categoryMap = new Map();\n this._storage.set(cat, categoryMap);\n }\n return categoryMap;\n }\n\n public async GetItem(key: string, category?: string): Promise<string | null> {\n const categoryMap = this.getCategoryMap(category || DEFAULT_CATEGORY);\n return categoryMap.get(key) ?? null;\n }\n\n public async SetItem(key: string, value: string, category?: string): Promise<void> {\n const categoryMap = this.getCategoryMap(category || DEFAULT_CATEGORY);\n categoryMap.set(key, value);\n }\n\n public async Remove(key: string, category?: string): Promise<void> {\n const categoryMap = this.getCategoryMap(category || DEFAULT_CATEGORY);\n categoryMap.delete(key);\n }\n\n public async ClearCategory(category: string): Promise<void> {\n const cat = category || DEFAULT_CATEGORY;\n this._storage.delete(cat);\n }\n\n public async GetCategoryKeys(category: string): Promise<string[]> {\n const categoryMap = this._storage.get(category || DEFAULT_CATEGORY);\n return categoryMap ? Array.from(categoryMap.keys()) : [];\n }\n}\n\n// ============================================================================\n// BROWSER LOCAL STORAGE PROVIDER (Key Prefix)\n// ============================================================================\n\n/**\n * Browser localStorage provider with category support via key prefixing.\n *\n * Key format: [mj]:[category]:[key]\n * Example: [mj]:[RunViewCache]:[Users|Active=1|Name ASC]\n *\n * Falls back to in-memory storage if localStorage is not available.\n */\nclass BrowserLocalStorageProvider extends BrowserStorageProviderBase {\n /**\n * Builds a prefixed key for localStorage\n * Format: [mj]:[category]:[key]\n */\n private buildKey(key: string, category?: string): string {\n const cat = category || DEFAULT_CATEGORY;\n return `[mj]:[${cat}]:[${key}]`;\n }\n\n /**\n * Parses a prefixed key to extract category and original key\n */\n private parseKey(prefixedKey: string): { category: string; key: string } | null {\n const match = prefixedKey.match(/^\\[mj\\]:\\[([^\\]]*)\\]:\\[(.+)\\]$/);\n if (match) {\n return { category: match[1], key: match[2] };\n }\n return null;\n }\n\n public override async GetItem(key: string, category?: string): Promise<string | null> {\n if (typeof localStorage !== 'undefined') {\n return localStorage.getItem(this.buildKey(key, category));\n }\n return await super.GetItem(key, category);\n }\n\n public override async SetItem(key: string, value: string, category?: string): Promise<void> {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(this.buildKey(key, category), value);\n } else {\n await super.SetItem(key, value, category);\n }\n }\n\n public override async Remove(key: string, category?: string): Promise<void> {\n if (typeof localStorage !== 'undefined') {\n localStorage.removeItem(this.buildKey(key, category));\n } else {\n await super.Remove(key, category);\n }\n }\n\n public override async ClearCategory(category: string): Promise<void> {\n if (typeof localStorage !== 'undefined') {\n const cat = category || DEFAULT_CATEGORY;\n const prefix = `[mj]:[${cat}]:`;\n const keysToRemove: string[] = [];\n\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key && key.startsWith(prefix)) {\n keysToRemove.push(key);\n }\n }\n\n for (const key of keysToRemove) {\n localStorage.removeItem(key);\n }\n } else {\n await super.ClearCategory(category);\n }\n }\n\n public override async GetCategoryKeys(category: string): Promise<string[]> {\n if (typeof localStorage !== 'undefined') {\n const cat = category || DEFAULT_CATEGORY;\n const prefix = `[mj]:[${cat}]:`;\n const keys: string[] = [];\n\n for (let i = 0; i < localStorage.length; i++) {\n const prefixedKey = localStorage.key(i);\n if (prefixedKey && prefixedKey.startsWith(prefix)) {\n const parsed = this.parseKey(prefixedKey);\n if (parsed) {\n keys.push(parsed.key);\n }\n }\n }\n\n return keys;\n }\n return await super.GetCategoryKeys(category);\n }\n}\n\n// ============================================================================\n// INDEXED DB STORAGE PROVIDER (Object Stores per Category)\n// ============================================================================\n\nconst IDB_DB_NAME = 'MJ_Metadata';\nconst IDB_DB_VERSION = 3; // v3: Remove legacy Metadata_KVPairs store\n\n// Known object store names as a const tuple for type safety\nconst KNOWN_OBJECT_STORES = [\n 'mj:default', // Default category\n 'mj:Metadata', // Metadata cache\n 'mj:RunViewCache', // RunView results cache\n 'mj:RunQueryCache', // RunQuery results cache\n 'mj:DatasetCache', // Dataset cache\n] as const;\n\n// Type for known store names\ntype KnownStoreName = typeof KNOWN_OBJECT_STORES[number];\n\n// Legacy store name - kept for cleanup during upgrade\nconst LEGACY_STORE_NAME = 'Metadata_KVPairs';\n\n/**\n * IndexedDB schema with dynamic object stores per category.\n * Each category gets its own object store: mj:CategoryName\n */\nexport interface MJ_MetadataDB extends DBSchema {\n // Default category store\n 'mj:default': {\n key: string;\n value: string;\n };\n // Metadata store\n 'mj:Metadata': {\n key: string;\n value: string;\n };\n // RunView cache store\n 'mj:RunViewCache': {\n key: string;\n value: string;\n };\n // RunQuery cache store\n 'mj:RunQueryCache': {\n key: string;\n value: string;\n };\n // Dataset cache store\n 'mj:DatasetCache': {\n key: string;\n value: string;\n };\n}\n\n/**\n * IndexedDB storage provider with category support via separate object stores.\n *\n * Known categories (mj:Metadata, mj:RunViewCache, etc.) get dedicated object stores.\n * Unknown categories fall back to the default store with prefixed keys.\n */\nexport class BrowserIndexedDBStorageProvider extends BrowserStorageProviderBase {\n private dbPromise: Promise<IDBPDatabase<MJ_MetadataDB>>;\n private _dbReady: boolean = false;\n\n constructor() {\n super();\n this.dbPromise = openDB<MJ_MetadataDB>(IDB_DB_NAME, IDB_DB_VERSION, {\n upgrade(db) {\n try {\n // Remove legacy store if it exists (cleanup from v1/v2)\n // Cast needed because LEGACY_STORE_NAME is not in current schema (it's being removed)\n if (db.objectStoreNames.contains(LEGACY_STORE_NAME as KnownStoreName)) {\n db.deleteObjectStore(LEGACY_STORE_NAME as KnownStoreName);\n }\n\n // Create known category stores\n for (const storeName of KNOWN_OBJECT_STORES) {\n if (!db.objectStoreNames.contains(storeName)) {\n db.createObjectStore(storeName);\n }\n }\n } catch (e) {\n LogErrorEx({\n error: e,\n message: (e as Error)?.message\n });\n }\n },\n });\n\n this.dbPromise.then(() => {\n this._dbReady = true;\n }).catch(e => {\n LogErrorEx({\n error: e,\n message: 'IndexedDB initialization failed: ' + (e as Error)?.message\n });\n });\n }\n\n /**\n * Checks if a category has a dedicated object store\n */\n private isKnownCategory(category: string): boolean {\n const storeName = `mj:${category}`;\n return (KNOWN_OBJECT_STORES as readonly string[]).includes(storeName);\n }\n\n /**\n * Gets the object store name for a category.\n * Returns the dedicated store if it exists, otherwise returns the default store.\n */\n private getStoreName(category?: string): KnownStoreName {\n const cat = category || DEFAULT_CATEGORY;\n if (this.isKnownCategory(cat)) {\n return `mj:${cat}` as KnownStoreName;\n }\n return 'mj:default';\n }\n\n /**\n * Gets the key to use in the store.\n * For known stores, use the key as-is.\n * For unknown categories using the default store, prefix with category.\n */\n private getStoreKey(key: string, category?: string): string {\n const cat = category || DEFAULT_CATEGORY;\n if (this.isKnownCategory(cat)) {\n return key;\n }\n // If using default store for unknown category, prefix the key\n return `[${cat}]:${key}`;\n }\n\n public override async SetItem(key: string, value: string, category?: string): Promise<void> {\n try {\n const db = await this.dbPromise;\n const storeName = this.getStoreName(category);\n const storeKey = this.getStoreKey(key, category);\n\n const tx = db.transaction(storeName, 'readwrite');\n await tx.objectStore(storeName).put(value, storeKey);\n await tx.done;\n } catch (e) {\n LogErrorEx({\n error: e,\n message: (e as Error)?.message\n });\n // Fall back to in-memory\n await super.SetItem(key, value, category);\n }\n }\n\n public override async GetItem(key: string, category?: string): Promise<string | null> {\n try {\n const db = await this.dbPromise;\n const storeName = this.getStoreName(category);\n const storeKey = this.getStoreKey(key, category);\n\n const value = await db.transaction(storeName).objectStore(storeName).get(storeKey);\n return value ?? null;\n } catch (e) {\n LogErrorEx({\n error: e,\n message: (e as Error)?.message\n });\n // Fall back to in-memory\n return await super.GetItem(key, category);\n }\n }\n\n public override async Remove(key: string, category?: string): Promise<void> {\n try {\n const db = await this.dbPromise;\n const storeName = this.getStoreName(category);\n const storeKey = this.getStoreKey(key, category);\n\n const tx = db.transaction(storeName, 'readwrite');\n await tx.objectStore(storeName).delete(storeKey);\n await tx.done;\n } catch (e) {\n LogErrorEx({\n error: e,\n message: (e as Error)?.message\n });\n // Fall back to in-memory\n await super.Remove(key, category);\n }\n }\n\n public override async ClearCategory(category: string): Promise<void> {\n try {\n const db = await this.dbPromise;\n const cat = category || DEFAULT_CATEGORY;\n const storeName = this.getStoreName(category);\n\n // If it's a dedicated store, clear the entire store\n if (this.isKnownCategory(cat)) {\n const tx = db.transaction(storeName, 'readwrite');\n await tx.objectStore(storeName).clear();\n await tx.done;\n } else {\n // For unknown categories using default store, clear only prefixed keys\n const prefix = `[${cat}]:`;\n const tx = db.transaction('mj:default', 'readwrite');\n const store = tx.objectStore('mj:default');\n const allKeys = await store.getAllKeys();\n\n for (const storeKey of allKeys) {\n if (typeof storeKey === 'string' && storeKey.startsWith(prefix)) {\n await store.delete(storeKey);\n }\n }\n await tx.done;\n }\n } catch (e) {\n LogErrorEx({\n error: e,\n message: (e as Error)?.message\n });\n // Fall back to in-memory\n await super.ClearCategory(category);\n }\n }\n\n public override async GetCategoryKeys(category: string): Promise<string[]> {\n try {\n const db = await this.dbPromise;\n const cat = category || DEFAULT_CATEGORY;\n const storeName = this.getStoreName(category);\n\n const tx = db.transaction(storeName, 'readonly');\n const store = tx.objectStore(storeName);\n const allKeys = await store.getAllKeys();\n\n // If it's a dedicated store, return keys as-is\n if (this.isKnownCategory(cat)) {\n return allKeys.map(k => String(k));\n }\n\n // For unknown categories, filter and strip prefix\n const prefix = `[${cat}]:`;\n return allKeys\n .map(k => String(k))\n .filter(k => k.startsWith(prefix))\n .map(k => k.slice(prefix.length));\n } catch (e) {\n LogErrorEx({\n error: e,\n message: (e as Error)?.message\n });\n // Fall back to in-memory\n return await super.GetCategoryKeys(category);\n }\n }\n}\n","/**************************************************************************************************************\n * The graphQLDataProvider provides a data provider for the entities framework that uses GraphQL to communicate\n * with the server.\n * In practice - this FILE will NOT exist in the entities library, we need to move to its own separate project\n * so it is only included by the consumer of the entities library if they want to use it.\n**************************************************************************************************************/\n\nimport { BaseEntity, IEntityDataProvider, IMetadataProvider, IRunViewProvider, ProviderConfigDataBase, RunViewResult,\n EntityInfo, EntityFieldInfo, EntityFieldTSType,\n RunViewParams, ProviderBase, ProviderType, UserInfo, UserRoleInfo, RecordChange,\n ILocalStorageProvider, EntitySaveOptions, EntityMergeOptions, LogError,\n TransactionGroupBase, TransactionItem, DatasetItemFilterType, DatasetResultType, DatasetStatusResultType, EntityRecordNameInput,\n EntityRecordNameResult, IRunReportProvider, RunReportResult, RunReportParams, RecordDependency, RecordMergeRequest, RecordMergeResult,\n RunQueryResult, PotentialDuplicateRequest, PotentialDuplicateResponse, CompositeKey, EntityDeleteOptions,\n RunQueryParams, BaseEntityResult,\n RunViewWithCacheCheckParams, RunViewsWithCacheCheckResponse, RunViewWithCacheCheckResult,\n RunQueryWithCacheCheckParams, RunQueriesWithCacheCheckResponse, RunQueryWithCacheCheckResult,\n KeyValuePair, getGraphQLTypeNameBase, AggregateExpression, InMemoryLocalStorageProvider } from \"@memberjunction/core\";\nimport { MJUserViewEntityExtended, ViewInfo } from '@memberjunction/core-entities'\n\nimport { gql, GraphQLClient } from 'graphql-request'\nimport { Observable, Subject, Subscription } from 'rxjs';\nimport { Client, createClient } from 'graphql-ws';\nimport { FieldMapper } from './FieldMapper';\nimport { v4 as uuidv4 } from 'uuid';\nimport { GraphQLTransactionGroup } from \"./graphQLTransactionGroup\";\nimport { GraphQLAIClient } from \"./graphQLAIClient\";\nimport { BrowserIndexedDBStorageProvider } from \"./storage-providers\";\n\n// define the shape for a RefreshToken function that can be called by the GraphQLDataProvider whenever it receives an exception that the JWT it has already is expired\nexport type RefreshTokenFunction = () => Promise<string>;\n\n/**\n * The GraphQLProviderConfigData class is used to configure the GraphQLDataProvider. It is passed to the Config method of the GraphQLDataProvider\n */\nexport class GraphQLProviderConfigData extends ProviderConfigDataBase {\n /**\n * Token is the JWT token that is used to authenticate the user with the server\n */\n get Token(): string { return this.Data.Token }\n\n set Token(token: string) { this.Data.Token = token}\n\n /**\n * This optional parameter is used when using a shared secret key that is static and provided by the publisher of the MJAPI server. Providing this value will result in\n * a special header x-mj-api-key being set with this value in the HTTP request to the server. This is useful when the server is configured to require this key for certain requests.\n *\n * WARNING: This should NEVER BE USED IN A CLIENT APP like a browser. The only suitable use for this is if you are using GraphQLDataProvider on the server side from another MJAPI, or\n * some other secure computing environment where the key can be kept secure.\n */\n get MJAPIKey(): string { return this.Data.MJAPIKey }\n set MJAPIKey(key: string) { this.Data.MJAPIKey = key }\n\n /**\n * This optional parameter is used when authenticating with a MemberJunction user API key (format: mj_sk_*).\n * When provided, it will be sent in the X-API-Key header. This authenticates as the specific user who owns the API key.\n *\n * Unlike MJAPIKey (system key), this is a user-specific key that can be used for automated access on behalf of a user.\n * Use this when you want to make API calls as a specific user without requiring OAuth authentication.\n */\n get UserAPIKey(): string { return this.Data.UserAPIKey }\n set UserAPIKey(key: string) { this.Data.UserAPIKey = key }\n\n /**\n * URL is the URL to the GraphQL endpoint\n */\n get URL(): string { return this.Data.URL }\n /**\n * WSURL is the URL to the GraphQL websocket endpoint. This is used for subscriptions, if you are not using subscriptions, you can pass in a blank string for this\n */\n get WSURL(): string { return this.Data.WSURL }\n\n /**\n * RefreshTokenFunction is a function that can be called by the GraphQLDataProvider whenever it receives an exception that the JWT it has already is expired\n */\n get RefreshTokenFunction(): RefreshTokenFunction { return this.Data.RefreshFunction }\n\n\n /**\n *\n * @param token Token is the JWT token that is used to authenticate the user with the server\n * @param url the URL to the GraphQL endpoint\n * @param wsurl the URL to the GraphQL websocket endpoint. This is used for subscriptions, if you are not using subscriptions, you can pass in a blank string for this\n * @param refreshTokenFunction is a function that can be called by the GraphQLDataProvider whenever it receives an exception that the JWT it has already is expired\n * @param MJCoreSchemaName the name of the MJ Core schema, if it is not the default name of __mj\n * @param includeSchemas optional, an array of schema names to include in the metadata. If not passed, all schemas are included\n * @param excludeSchemas optional, an array of schema names to exclude from the metadata. If not passed, no schemas are excluded\n * @param mjAPIKey optional, a shared secret key that is static and provided by the publisher of the MJAPI server.\n * @param userAPIKey optional, a user-specific API key (mj_sk_* format) for authenticating as a specific user\n */\n constructor(token: string,\n url: string,\n wsurl: string,\n refreshTokenFunction: RefreshTokenFunction,\n MJCoreSchemaName?: string,\n includeSchemas?: string[],\n excludeSchemas?: string[],\n mjAPIKey?: string,\n userAPIKey?: string) {\n super(\n {\n Token: token,\n URL: url,\n WSURL: wsurl,\n MJAPIKey: mjAPIKey,\n UserAPIKey: userAPIKey,\n RefreshTokenFunction: refreshTokenFunction,\n },\n MJCoreSchemaName,\n includeSchemas,\n excludeSchemas\n );\n }\n}\n\n\n\n// The GraphQLDataProvider implements both the IEntityDataProvider and IMetadataProvider interfaces.\n/**\n * The GraphQLDataProvider class is a data provider for MemberJunction that implements the IEntityDataProvider, IMetadataProvider, IRunViewProvider, IRunReportProvider, IRunQueryProvider interfaces and connects to the\n * MJAPI server using GraphQL. This class is used to interact with the server to get and save data, as well as to get metadata about the entities and fields in the system.\n */\nexport class GraphQLDataProvider extends ProviderBase implements IEntityDataProvider, IMetadataProvider, IRunReportProvider {\n private static _instance: GraphQLDataProvider;\n public static get Instance(): GraphQLDataProvider {\n return GraphQLDataProvider._instance;\n }\n\n constructor() {\n super();\n if (!GraphQLDataProvider._instance)\n GraphQLDataProvider._instance = this;\n }\n\n private _client: GraphQLClient;\n private _configData: GraphQLProviderConfigData;\n private _sessionId: string;\n private _aiClient: GraphQLAIClient;\n private _refreshPromise: Promise<void> | null = null;\n\n public get ConfigData(): GraphQLProviderConfigData {\n return this._configData;\n }\n\n\n /**\n * Gets the AI client for executing AI operations through GraphQL.\n * The client is lazily initialized on first access.\n * @returns The GraphQLAIClient instance\n */\n public get AI(): GraphQLAIClient {\n if (!this._aiClient) {\n this._aiClient = new GraphQLAIClient(this);\n }\n return this._aiClient;\n }\n\n /**\n * This getter is not implemented for the GraphQLDataProvider class.\n */\n public get DatabaseConnection(): any {\n throw new Error(\"DatabaseConnection not implemented for the GraphQLDataProvider\");\n }\n\n /**\n * The connection string for each GraphQLProvider instance is simply the URL for the GraphQL endpoint. This is because each GraphQLDataProvider instance can be configured with a different URL and each URL\n * is a unique combination of host/port/etc.\n */\n public get InstanceConnectionString(): string {\n return this._configData.URL\n }\n\n public GenerateUUID() {\n return uuidv4();\n }\n\n /**\n * The GraphQLDataProvider uses a prefix for local storage that is equal to the URL of the GraphQL endpoint. This is because the GraphQLDataProvider can be configured multiple times with different URLs and each\n * configuration will have its own local storage. This is useful when you want to have multiple connections to different servers and you don't want the local storage to be shared between them. The URL is \n * normalized to remove special characters and replace anything other than alphanumeric characters with an underscore.\n */\n protected override get LocalStoragePrefix(): string {\n if (this._configData === undefined || this._configData.URL === undefined) {\n throw new Error(\"GraphQLDataProvider: ConfigData is not set. Please call Config() first.\");\n }\n\n const replacementString = this._configData.URL.replace(/[^a-zA-Z0-9]/g, '_');\n return replacementString + \".\"; // add a period at the end to separate the prefix from the key\n }\n\n /**\n * Retrieves the stored session ID from the LocalStorageProvider if available.\n * If no session ID is found, returns null.\n * The session ID is stored using the same storage mechanism as other persistent data\n * with a key specific to the current URL to ensure uniqueness across different \n * server connections.\n * \n * @returns The stored session ID or null if not found\n */\n public async GetStoredSessionID(): Promise<string> {\n try {\n const ls = this.LocalStorageProvider;\n if (ls) {\n const key = this.LocalStoragePrefix + \"sessionId\";\n const storedSession = await ls.GetItem(key);\n return storedSession;\n }\n return null;\n } catch (e) {\n // If any error occurs, return null\n console.error(\"Error retrieving session ID from local storage:\", e);\n return null;\n }\n }\n\n /**\n * Stores the session ID using the configured LocalStorageProvider for persistence.\n * Uses the same URL-specific key pattern as other storage methods to ensure\n * proper isolation between different server connections.\n * \n * @param sessionId The session ID to store\n */\n private async SaveStoredSessionID(sessionId: string): Promise<void> {\n try {\n const ls = this.LocalStorageProvider;\n if (ls) {\n const key = this.LocalStoragePrefix + \"sessionId\";\n await ls.SetItem(key, sessionId);\n }\n } catch (e) {\n // Silently fail if storage is not available\n }\n }\n\n public async GetPreferredUUID(forceRefreshSessionId?: boolean): Promise<string> {\n // Try to get the stored session ID\n const oldUUID = await this.GetStoredSessionID();\n const UUID = forceRefreshSessionId || !oldUUID ? this.GenerateUUID() : oldUUID;\n return UUID;\n }\n\n\n /**\n * This method configures the class instance. If separateConnection is false or not provided, the global/static variables are set that means that the Config() call\n * will affect all callers to the GraphQLDataProvider including via wrappers like the Metadata class. If separateConnection is true, then the instance variables are set\n * and only this instance of the GraphQLDataProvider will be affected by the Config() call.\n * @important If separateConnection is true, metadata for the provider will be loaded but will NOT affect the Metadata class/singleton. \n * This is because the Metadata class is a singleton that binds to the first Config() call in the process where separateConnection is falsy. \n * @param configData \n * @param separateConnection \n * @returns \n */\n public async Config(configData: GraphQLProviderConfigData, providerToUse?: IMetadataProvider, separateConnection?: boolean, forceRefreshSessionId?: boolean): Promise<boolean> {\n try {\n // Enhanced logging to diagnose token issues\n // const tokenPreview = configData.Token ? `${configData.Token.substring(0, 20)}...${configData.Token.substring(configData.Token.length - 10)}` : 'NO TOKEN';\n // console.log('[GraphQL] Config called with token:', {\n // tokenPreview,\n // tokenLength: configData.Token?.length,\n // separateConnection,\n // hasRefreshFunction: !!configData.Data?.RefreshTokenFunction\n // });\n\n // CRITICAL: Always set this instance's _configData first\n // This ensures BuildDatasetFilterFromConfig() can access ConfigData.IncludeSchemas\n this._configData = configData;\n\n if (separateConnection) {\n // Get UUID after setting the configData, so that it can be used to get any stored session ID\n this._sessionId = await this.GetPreferredUUID(forceRefreshSessionId);;\n\n this._client = this.CreateNewGraphQLClient(configData.URL, configData.Token, this._sessionId, configData.MJAPIKey, configData.UserAPIKey);\n // Store the session ID for this connection\n await this.SaveStoredSessionID(this._sessionId);\n }\n else {\n // Update the singleton instance\n GraphQLDataProvider.Instance._configData = configData;\n\n if (GraphQLDataProvider.Instance._sessionId === undefined) {\n GraphQLDataProvider.Instance._sessionId = await this.GetPreferredUUID(forceRefreshSessionId);;\n }\n\n // now create the new client, if it isn't already created\n if (!GraphQLDataProvider.Instance._client)\n GraphQLDataProvider.Instance._client = this.CreateNewGraphQLClient(configData.URL, configData.Token, GraphQLDataProvider.Instance._sessionId, configData.MJAPIKey, configData.UserAPIKey);\n\n // Store the session ID for the global instance\n await GraphQLDataProvider.Instance.SaveStoredSessionID(GraphQLDataProvider.Instance._sessionId);\n\n // CRITICAL: Sync this instance with the singleton\n // This ensures ExecuteGQL() can use this._client.request()\n this._sessionId = GraphQLDataProvider.Instance._sessionId;\n this._client = GraphQLDataProvider.Instance._client;\n }\n return super.Config(configData); // now parent class can do it's config\n }\n catch (e) {\n LogError(e);\n throw (e)\n }\n }\n\n public get sessionId(): string {\n return this._sessionId;\n }\n\n protected get AllowRefresh(): boolean {\n return true; // this provider doesn't have any issues with allowing refreshes at any time\n }\n\n protected async GetCurrentUser(): Promise<UserInfo> {\n const d = await this.ExecuteGQL(this._currentUserQuery, null);\n if (d) {\n // convert the user and the user roles _mj__*** fields back to __mj_***\n const u = this.ConvertBackToMJFields(d.CurrentUser);\n const roles = u.MJUserRoles_UserIDArray.map(r => this.ConvertBackToMJFields(r));\n u.MJUserRoles_UserIDArray = roles;\n return new UserInfo(this, {...u, UserRoles: roles}) // need to pass in the UserRoles as a separate property that is what is expected here\n }\n }\n\n\n /**************************************************************************/\n // START ---- IRunReportProvider\n /**************************************************************************/\n public async RunReport(params: RunReportParams, contextUser?: UserInfo): Promise<RunReportResult> {\n const query = gql`\n query GetReportDataQuery ($ReportID: String!) {\n GetReportData(ReportID: $ReportID) {\n Success\n Results\n RowCount\n ExecutionTime\n ErrorMessage\n }\n }`\n\n const result = await this.ExecuteGQL(query, {ReportID: params.ReportID} );\n if (result && result.GetReportData)\n return {\n ReportID: params.ReportID,\n Success: result.GetReportData.Success,\n Results: JSON.parse(result.GetReportData.Results),\n RowCount: result.GetReportData.RowCount,\n ExecutionTime: result.GetReportData.ExecutionTime,\n ErrorMessage: result.GetReportData.ErrorMessage,\n };\n }\n /**************************************************************************/\n // END ---- IRunReportProvider\n /**************************************************************************/\n\n /**************************************************************************/\n // START ---- IRunQueryProvider\n /**************************************************************************/\n protected async InternalRunQuery(params: RunQueryParams, contextUser?: UserInfo): Promise<RunQueryResult> {\n // This is the internal implementation - pre/post processing is handled by ProviderBase.RunQuery()\n if (params.SQL) {\n return this.RunAdhocQuery(params.SQL, params.MaxRows);\n }\n else if (params.QueryID) {\n return this.RunQueryByID(params.QueryID, params.CategoryID, params.CategoryPath, contextUser, params.Parameters, params.MaxRows, params.StartRow);\n }\n else if (params.QueryName) {\n return this.RunQueryByName(params.QueryName, params.CategoryID, params.CategoryPath, contextUser, params.Parameters, params.MaxRows, params.StartRow);\n }\n else {\n throw new Error(\"No SQL, QueryID, or QueryName provided to RunQuery\");\n }\n }\n\n /**\n * Executes an ad-hoc SQL query via the ExecuteAdhocQuery GraphQL resolver.\n * The server validates the SQL (SELECT/WITH only) and executes on a read-only connection.\n */\n protected async RunAdhocQuery(sql: string, maxRows?: number, timeoutSeconds?: number): Promise<RunQueryResult> {\n const query = gql`\n query ExecuteAdhocQuery($input: AdhocQueryInput!) {\n ExecuteAdhocQuery(input: $input) {\n ${this.QueryReturnFieldList}\n }\n }\n `;\n\n const input: { SQL: string; TimeoutSeconds?: number } = { SQL: sql };\n if (timeoutSeconds !== undefined) {\n input.TimeoutSeconds = timeoutSeconds;\n }\n\n const result = await this.ExecuteGQL(query, { input });\n if (result?.ExecuteAdhocQuery) {\n return this.TransformQueryPayload(result.ExecuteAdhocQuery);\n }\n return {\n QueryID: '',\n QueryName: 'Ad-Hoc Query',\n Success: false,\n Results: [],\n RowCount: 0,\n TotalRowCount: 0,\n ExecutionTime: 0,\n ErrorMessage: 'Ad-hoc query execution failed — no response from server'\n };\n }\n\n protected async InternalRunQueries(params: RunQueryParams[], contextUser?: UserInfo): Promise<RunQueryResult[]> {\n // This is the internal implementation - pre/post processing is handled by ProviderBase.RunQueries()\n // Make a single batch GraphQL call for efficiency (single network roundtrip)\n const query = gql`\n query RunQueriesBatch($input: [RunQueryInput!]!) {\n RunQueries(input: $input) {\n ${this.QueryReturnFieldList}\n }\n }\n `;\n\n // Convert params to the input format expected by the GraphQL resolver\n const input = params.map(p => ({\n QueryID: p.QueryID,\n QueryName: p.QueryName,\n CategoryID: p.CategoryID,\n CategoryPath: p.CategoryPath,\n Parameters: p.Parameters,\n MaxRows: p.MaxRows,\n StartRow: p.StartRow,\n ForceAuditLog: p.ForceAuditLog,\n AuditLogDescription: p.AuditLogDescription\n }));\n\n const result = await this.ExecuteGQL(query, { input });\n if (result && result.RunQueries) {\n // Transform each result in the batch\n return result.RunQueries.map((r: unknown) => this.TransformQueryPayload(r));\n }\n return [];\n }\n\n public async RunQueryByID(QueryID: string, CategoryID?: string, CategoryPath?: string, contextUser?: UserInfo, Parameters?: Record<string, any>, MaxRows?: number, StartRow?: number): Promise<RunQueryResult> {\n const query = gql`\n query GetQueryDataQuery($QueryID: String!, $CategoryID: String, $CategoryPath: String, $Parameters: JSONObject, $MaxRows: Int, $StartRow: Int) {\n GetQueryData(QueryID: $QueryID, CategoryID: $CategoryID, CategoryPath: $CategoryPath, Parameters: $Parameters, MaxRows: $MaxRows, StartRow: $StartRow) {\n ${this.QueryReturnFieldList}\n }\n }\n `;\n \n // Build the variables object, adding optional parameters if defined.\n const variables: { QueryID: string; CategoryID?: string; CategoryPath?: string; Parameters?: Record<string, any>; MaxRows?: number; StartRow?: number } = { QueryID };\n if (CategoryID !== undefined) {\n variables.CategoryID = CategoryID;\n }\n if (CategoryPath !== undefined) {\n variables.CategoryPath = CategoryPath;\n }\n if (Parameters !== undefined) {\n variables.Parameters = Parameters;\n }\n if (MaxRows !== undefined) {\n variables.MaxRows = MaxRows;\n }\n if (StartRow !== undefined) {\n variables.StartRow = StartRow;\n }\n \n const result = await this.ExecuteGQL(query, variables);\n if (result && result.GetQueryData) {\n return this.TransformQueryPayload(result.GetQueryData);\n }\n }\n \n public async RunQueryByName(QueryName: string, CategoryID?: string, CategoryPath?: string, contextUser?: UserInfo, Parameters?: Record<string, any>, MaxRows?: number, StartRow?: number): Promise<RunQueryResult> {\n const query = gql`\n query GetQueryDataByNameQuery($QueryName: String!, $CategoryID: String, $CategoryPath: String, $Parameters: JSONObject, $MaxRows: Int, $StartRow: Int) {\n GetQueryDataByName(QueryName: $QueryName, CategoryID: $CategoryID, CategoryPath: $CategoryPath, Parameters: $Parameters, MaxRows: $MaxRows, StartRow: $StartRow) {\n ${this.QueryReturnFieldList}\n }\n }\n `;\n \n // Build the variables object, adding optional parameters if defined.\n const variables: { QueryName: string; CategoryID?: string; CategoryPath?: string; Parameters?: Record<string, any>; MaxRows?: number; StartRow?: number } = { QueryName };\n if (CategoryID !== undefined) {\n variables.CategoryID = CategoryID;\n }\n if (CategoryPath !== undefined) {\n variables.CategoryPath = CategoryPath;\n }\n if (Parameters !== undefined) {\n variables.Parameters = Parameters;\n }\n if (MaxRows !== undefined) {\n variables.MaxRows = MaxRows;\n }\n if (StartRow !== undefined) {\n variables.StartRow = StartRow;\n }\n \n const result = await this.ExecuteGQL(query, variables);\n if (result && result.GetQueryDataByName) {\n return this.TransformQueryPayload(result.GetQueryDataByName);\n }\n }\n\n protected get QueryReturnFieldList(): string {\n return `\n Success\n QueryID\n QueryName\n Results\n RowCount\n TotalRowCount\n ExecutionTime\n ErrorMessage\n AppliedParameters`\n }\n protected TransformQueryPayload(data: any): RunQueryResult {\n try {\n return {\n QueryID: data.QueryID,\n QueryName: data.QueryName,\n Success: data.Success,\n Results: JSON.parse(data.Results),\n RowCount: data.RowCount,\n TotalRowCount: data.TotalRowCount,\n ExecutionTime: data.ExecutionTime,\n ErrorMessage: data.ErrorMessage,\n AppliedParameters: data.AppliedParameters ? JSON.parse(data.AppliedParameters) : undefined\n }; \n }\n catch (e) {\n LogError(`Error transforming query payload: ${e}`);\n return null;\n }\n }\n\n /**\n * RunQueriesWithCacheCheck - Smart cache validation for batch RunQueries.\n * For each query, if cacheStatus is provided, the server checks if the cache is current\n * using the Query's CacheValidationSQL. If current, returns status='current' with no data.\n * If stale, returns status='stale' with fresh data.\n *\n * @param params - Array of RunQuery requests with optional cache status\n * @param contextUser - Optional user context\n * @returns Response containing results for each query in the batch\n */\n public async RunQueriesWithCacheCheck<T = unknown>(\n params: RunQueryWithCacheCheckParams[],\n contextUser?: UserInfo\n ): Promise<RunQueriesWithCacheCheckResponse<T>> {\n try {\n // Build the GraphQL input\n const input = params.map(item => ({\n params: {\n QueryID: item.params.QueryID || null,\n QueryName: item.params.QueryName || null,\n CategoryID: item.params.CategoryID || null,\n CategoryPath: item.params.CategoryPath || null,\n Parameters: item.params.Parameters || null,\n MaxRows: item.params.MaxRows ?? null,\n StartRow: item.params.StartRow ?? null,\n ForceAuditLog: item.params.ForceAuditLog || false,\n AuditLogDescription: item.params.AuditLogDescription || null,\n },\n cacheStatus: item.cacheStatus ? {\n maxUpdatedAt: item.cacheStatus.maxUpdatedAt,\n rowCount: item.cacheStatus.rowCount,\n } : null,\n }));\n\n const query = gql`\n query RunQueriesWithCacheCheckQuery($input: [RunQueryWithCacheCheckInput!]!) {\n RunQueriesWithCacheCheck(input: $input) {\n success\n errorMessage\n results {\n queryIndex\n queryId\n status\n Results\n maxUpdatedAt\n rowCount\n errorMessage\n }\n }\n }\n `;\n\n const responseData = await this.ExecuteGQL(query, { input });\n const response = responseData?.['RunQueriesWithCacheCheck'] as {\n success: boolean;\n errorMessage?: string;\n results: Array<{\n queryIndex: number;\n queryId: string;\n status: string;\n Results?: string;\n maxUpdatedAt?: string;\n rowCount?: number;\n errorMessage?: string;\n }>;\n };\n\n if (!response) {\n return {\n success: false,\n results: [],\n errorMessage: 'No response from server',\n };\n }\n\n // Transform results - deserialize Results for stale/no_validation results\n const transformedResults: RunQueryWithCacheCheckResult<T>[] = response.results.map(result => {\n if ((result.status === 'stale' || result.status === 'no_validation') && result.Results) {\n // Deserialize the Results JSON string\n const deserializedResults: T[] = JSON.parse(result.Results);\n\n return {\n queryIndex: result.queryIndex,\n queryId: result.queryId,\n status: result.status as 'current' | 'stale' | 'no_validation' | 'error',\n results: deserializedResults,\n maxUpdatedAt: result.maxUpdatedAt,\n rowCount: result.rowCount,\n errorMessage: result.errorMessage,\n };\n }\n\n return {\n queryIndex: result.queryIndex,\n queryId: result.queryId,\n status: result.status as 'current' | 'stale' | 'no_validation' | 'error',\n maxUpdatedAt: result.maxUpdatedAt,\n rowCount: result.rowCount,\n errorMessage: result.errorMessage,\n };\n });\n\n return {\n success: response.success,\n results: transformedResults,\n errorMessage: response.errorMessage,\n };\n } catch (e) {\n LogError(`Error in RunQueriesWithCacheCheck: ${e}`);\n return {\n success: false,\n results: [],\n errorMessage: e instanceof Error ? e.message : String(e),\n };\n }\n }\n\n /**************************************************************************/\n // END ---- IRunQueryProvider\n /**************************************************************************/\n\n\n\n /**************************************************************************/\n // START ---- IRunViewProvider\n /**************************************************************************/\n protected async InternalRunView<T = any>(params: RunViewParams, contextUser?: UserInfo): Promise<RunViewResult<T>> {\n // This is the internal implementation - pre/post processing is handled by ProviderBase.RunView()\n try {\n let qName: string = ''\n let paramType: string = ''\n if (params) {\n const innerParams: any = {}\n let entity: string, viewEntity: any;\n if (params.ViewEntity) {\n viewEntity = params.ViewEntity\n entity = viewEntity.Entity\n }\n else {\n const {entityName, v} = await this.getEntityNameAndUserView(params, contextUser)\n viewEntity = v;\n entity = entityName;\n }\n\n // get entity metadata\n const e = this.Entities.find(e => e.Name === entity);\n if (!e)\n throw new Error(`Entity ${entity} not found in metadata`);\n\n let dynamicView = false;\n const graphQLTypeName = getGraphQLTypeNameBase(e);\n\n if (params.ViewID) {\n qName = `Run${graphQLTypeName}ViewByID`;\n paramType = 'RunViewByIDInput';\n innerParams.ViewID = params.ViewID;\n }\n else if (params.ViewName) {\n qName = `Run${graphQLTypeName}ViewByName`;\n paramType = 'RunViewByNameInput';\n innerParams.ViewName = params.ViewName;\n }\n else {\n dynamicView = true;\n qName = `Run${graphQLTypeName}DynamicView`;\n paramType = 'RunDynamicViewInput';\n innerParams.EntityName = params.EntityName;\n }\n innerParams.ExtraFilter = params.ExtraFilter ? params.ExtraFilter : '';\n innerParams.OrderBy = params.OrderBy ? params.OrderBy : '';\n innerParams.UserSearchString = params.UserSearchString ? params.UserSearchString : '';\n innerParams.Fields = params.Fields; // pass it straight through, either null or array of strings\n innerParams.IgnoreMaxRows = params.IgnoreMaxRows ? params.IgnoreMaxRows : false;\n if (params.MaxRows !== undefined)\n innerParams.MaxRows = params.MaxRows;\n if (params.StartRow !== undefined)\n innerParams.StartRow = params.StartRow; // Add StartRow parameter\n innerParams.ForceAuditLog = params.ForceAuditLog ? params.ForceAuditLog : false;\n innerParams.ResultType = params.ResultType ? params.ResultType : 'simple';\n if (params.AuditLogDescription && params.AuditLogDescription.length > 0)\n innerParams.AuditLogDescription = params.AuditLogDescription;\n\n if (!dynamicView) {\n innerParams.ExcludeUserViewRunID = params.ExcludeUserViewRunID ? params.ExcludeUserViewRunID : \"\";\n innerParams.ExcludeDataFromAllPriorViewRuns = params.ExcludeDataFromAllPriorViewRuns ? params.ExcludeDataFromAllPriorViewRuns : false;\n innerParams.OverrideExcludeFilter = params.OverrideExcludeFilter ? params.OverrideExcludeFilter : '';\n innerParams.SaveViewResults = params.SaveViewResults ? params.SaveViewResults : false;\n }\n\n // Include Aggregates if provided\n if (params.Aggregates && params.Aggregates.length > 0) {\n innerParams.Aggregates = params.Aggregates.map((a: AggregateExpression) => ({\n expression: a.expression,\n alias: a.alias\n }));\n }\n\n const fieldList = this.getViewRunTimeFieldList(e, viewEntity, params, dynamicView);\n\n // Build aggregate fields for response if aggregates requested\n const aggregateResponseFields = params.Aggregates && params.Aggregates.length > 0\n ? `\n AggregateResults {\n alias\n expression\n value\n error\n }\n AggregateExecutionTime`\n : '';\n\n const query = gql`\n query RunViewQuery ($input: ${paramType}!) {\n ${qName}(input: $input) {\n Results {\n ${fieldList.join(\"\\n \")}\n }\n UserViewRunID\n RowCount\n TotalRowCount\n ExecutionTime\n Success\n ErrorMessage${aggregateResponseFields}\n }\n }`\n\n // Log aggregate request for debugging\n if (innerParams.Aggregates?.length > 0) {\n console.log('[GraphQLDataProvider] Sending RunView with aggregates:', {\n entityName: entity,\n queryName: qName,\n aggregateCount: innerParams.Aggregates.length,\n aggregates: innerParams.Aggregates\n });\n }\n\n const viewData = await this.ExecuteGQL(query, {input: innerParams} );\n if (viewData && viewData[qName]) {\n // Log aggregate response for debugging\n const responseAggregates = viewData[qName].AggregateResults;\n if (innerParams.Aggregates?.length > 0) {\n console.log('[GraphQLDataProvider] Received aggregate results:', {\n entityName: entity,\n aggregateResultCount: responseAggregates?.length || 0,\n aggregateResults: responseAggregates,\n aggregateExecutionTime: viewData[qName].AggregateExecutionTime\n });\n }\n\n // now, if we have any results in viewData that are for the CodeName, we need to convert them to the Name\n // so that the caller gets back what they expect\n const results = viewData[qName].Results;\n if (results && results.length > 0) {\n const codeNameDiffFields = e.Fields.filter(f => f.CodeName !== f.Name && f.CodeName !== undefined);\n results.forEach(r => {\n // for _mj__ results, we need to convert them back to the Name\n this.ConvertBackToMJFields(r);\n codeNameDiffFields.forEach(f => {\n r[f.Name] = r[f.CodeName];\n // delete r[f.CodeName]; // Leave the CodeName in the results, it is useful to have both\n })\n })\n }\n const result = viewData[qName];\n\n return result;\n }\n }\n else\n throw (\"No parameters passed to RunView\");\n\n return null;\n }\n catch (e) {\n LogError(e);\n throw (e)\n }\n }\n\n protected async InternalRunViews<T = any>(params: RunViewParams[], contextUser?: UserInfo): Promise<RunViewResult<T>[]> {\n\n try {\n let innerParams: any[] = [];\n let entityInfos: EntityInfo[] = [];\n let fieldList: string[] = [];\n\n for(const param of params){\n let qName: string = ''\n let paramType: string = ''\n const innerParam: any = {}\n let entity: string | null = null;\n let viewEntity: MJUserViewEntityExtended | null = null;\n if (param.ViewEntity) {\n viewEntity = param.ViewEntity as MJUserViewEntityExtended;\n entity = viewEntity.Get(\"Entity\");\n }\n else {\n const {entityName, v} = await this.getEntityNameAndUserView(param, contextUser)\n viewEntity = v;\n entity = entityName;\n }\n\n // get entity metadata\n const e = this.Entities.find(e => e.Name === entity);\n if (!e){\n throw new Error(`Entity ${entity} not found in metadata`);\n }\n\n entityInfos.push(e);\n let dynamicView: boolean = false;\n const graphQLTypeName = getGraphQLTypeNameBase(e);\n\n if (param.ViewID) {\n qName = `Run${graphQLTypeName}ViewByID`;\n paramType = 'RunViewByIDInput';\n innerParam.ViewID = param.ViewID;\n }\n else if (param.ViewName) {\n qName = `Run${graphQLTypeName}ViewByName`;\n paramType = 'RunViewByNameInput';\n innerParam.ViewName = param.ViewName;\n }\n else {\n dynamicView = true;\n qName = `Run${graphQLTypeName}DynamicView`;\n paramType = 'RunDynamicViewInput';\n innerParam.EntityName = param.EntityName;\n }\n\n innerParam.ExtraFilter = param.ExtraFilter || '';\n innerParam.OrderBy = param.OrderBy || '';\n innerParam.UserSearchString = param.UserSearchString || '';\n // pass it straight through, either null or array of strings\n innerParam.Fields = param.Fields;\n innerParam.IgnoreMaxRows = param.IgnoreMaxRows || false;\n if (param.MaxRows !== undefined)\n innerParam.MaxRows = param.MaxRows;\n if (param.StartRow !== undefined)\n innerParam.StartRow = param.StartRow; // Add StartRow parameter\n innerParam.ForceAuditLog = param.ForceAuditLog || false;\n innerParam.ResultType = param.ResultType || 'simple';\n if (param.AuditLogDescription && param.AuditLogDescription.length > 0){\n innerParam.AuditLogDescription = param.AuditLogDescription;\n }\n\n if (!dynamicView) {\n innerParam.ExcludeUserViewRunID = param.ExcludeUserViewRunID || \"\";\n innerParam.ExcludeDataFromAllPriorViewRuns = param.ExcludeDataFromAllPriorViewRuns || false;\n innerParam.OverrideExcludeFilter = param.OverrideExcludeFilter || '';\n innerParam.SaveViewResults = param.SaveViewResults || false;\n }\n\n // Include Aggregates if provided\n if (param.Aggregates && param.Aggregates.length > 0) {\n innerParam.Aggregates = param.Aggregates.map((a: AggregateExpression) => ({\n expression: a.expression,\n alias: a.alias\n }));\n }\n\n innerParams.push(innerParam);\n fieldList.push(...this.getViewRunTimeFieldList(e, viewEntity, param, dynamicView));\n }\n\n // Check if any view in the batch has aggregates\n const hasAnyAggregates = params.some(p => p.Aggregates && p.Aggregates.length > 0);\n const aggregateResponseFields = hasAnyAggregates\n ? `\n AggregateResults {\n alias\n expression\n value\n error\n }\n AggregateExecutionTime`\n : '';\n\n const query = gql`\n query RunViewsQuery ($input: [RunViewGenericInput!]!) {\n RunViews(input: $input) {\n Results {\n PrimaryKey {\n FieldName\n Value\n }\n EntityID\n Data\n }\n UserViewRunID\n RowCount\n TotalRowCount\n ExecutionTime\n Success\n ErrorMessage${aggregateResponseFields}\n }\n }`;\n\n const viewData: unknown = await this.ExecuteGQL(query, {input: innerParams} );\n if (viewData && viewData[\"RunViews\"]) {\n // now, if we have any results in viewData that are for the CodeName, we need to convert them to the Name\n // so that the caller gets back what they expect\n const results: RunViewResult[] = viewData[\"RunViews\"];\n for(const [index, result] of results.entries()){\n //const codeNameDiffFields = entityInfos[index].Fields.filter(f => f.CodeName !== f.Name && f.CodeName !== undefined);\n result.Results = result.Results.map((data: unknown) => {\n let deserializeData: Record<string, unknown> = JSON.parse(data[\"Data\"]);\n // for _mj__ results, we need to convert them back to the Name\n this.ConvertBackToMJFields(deserializeData);\n /*\n codeNameDiffFields.forEach(f => {\n deserializeData[f.Name] = deserializeData[f.CodeName];\n // delete r[f.CodeName]; // Leave the CodeName in the results, it is useful to have both\n });\n */\n return deserializeData;\n });\n }\n\n return results;\n }\n\n return null;\n\n }\n catch (e) {\n LogError(e);\n throw (e);\n }\n }\n\n /**\n * RunViewsWithCacheCheck - Smart cache validation for batch RunViews.\n * For each view, if cacheStatus is provided, the server checks if the cache is current.\n * If current, returns status='current' with no data. If stale, returns status='stale' with fresh data.\n *\n * @param params - Array of RunView requests with optional cache status\n * @param contextUser - Optional user context\n * @returns Response containing results for each view in the batch\n */\n public async RunViewsWithCacheCheck<T = unknown>(\n params: RunViewWithCacheCheckParams[],\n contextUser?: UserInfo\n ): Promise<RunViewsWithCacheCheckResponse<T>> {\n try {\n // Build the GraphQL input\n const input = params.map(item => ({\n params: {\n EntityName: item.params.EntityName || '',\n ExtraFilter: item.params.ExtraFilter || '',\n OrderBy: item.params.OrderBy || '',\n Fields: item.params.Fields,\n UserSearchString: item.params.UserSearchString || '',\n IgnoreMaxRows: item.params.IgnoreMaxRows || false,\n MaxRows: item.params.MaxRows,\n StartRow: item.params.StartRow,\n ForceAuditLog: item.params.ForceAuditLog || false,\n AuditLogDescription: item.params.AuditLogDescription || '',\n ResultType: item.params.ResultType || 'simple',\n },\n cacheStatus: item.cacheStatus ? {\n maxUpdatedAt: item.cacheStatus.maxUpdatedAt,\n rowCount: item.cacheStatus.rowCount,\n } : null,\n }));\n\n const query = gql`\n query RunViewsWithCacheCheckQuery($input: [RunViewWithCacheCheckInput!]!) {\n RunViewsWithCacheCheck(input: $input) {\n success\n errorMessage\n results {\n viewIndex\n status\n maxUpdatedAt\n rowCount\n errorMessage\n Results {\n PrimaryKey {\n FieldName\n Value\n }\n EntityID\n Data\n }\n differentialData {\n updatedRows {\n PrimaryKey {\n FieldName\n Value\n }\n EntityID\n Data\n }\n deletedRecordIDs\n }\n }\n }\n }\n `;\n\n const responseData = await this.ExecuteGQL(query, { input });\n const response = responseData?.['RunViewsWithCacheCheck'] as {\n success: boolean;\n errorMessage?: string;\n results: Array<{\n viewIndex: number;\n status: string;\n maxUpdatedAt?: string;\n rowCount?: number;\n errorMessage?: string;\n Results?: Array<{ PrimaryKey: Array<{ FieldName: string; Value: string }>; EntityID: string; Data: string }>;\n differentialData?: {\n updatedRows: Array<{ PrimaryKey: Array<{ FieldName: string; Value: string }>; EntityID: string; Data: string }>;\n deletedRecordIDs: string[];\n };\n }>;\n };\n\n if (!response) {\n return {\n success: false,\n results: [],\n errorMessage: 'No response from server',\n };\n }\n\n // Transform results - deserialize Data fields for stale/differential results\n const transformedResults: RunViewWithCacheCheckResult<T>[] = response.results.map((result, index) => {\n const inputItem = params[index];\n\n if (result.status === 'differential' && result.differentialData) {\n // Deserialize the differential data\n const deserializedUpdatedRows: T[] = result.differentialData.updatedRows.map(r => {\n const data = JSON.parse(r.Data);\n this.ConvertBackToMJFields(data);\n return data as T;\n });\n\n return {\n viewIndex: result.viewIndex,\n status: result.status as 'current' | 'stale' | 'differential' | 'error',\n results: undefined,\n differentialData: {\n updatedRows: deserializedUpdatedRows,\n deletedRecordIDs: result.differentialData.deletedRecordIDs,\n },\n maxUpdatedAt: result.maxUpdatedAt,\n rowCount: result.rowCount,\n errorMessage: result.errorMessage,\n };\n }\n\n if (result.status === 'stale' && result.Results) {\n // Deserialize the Data field and convert back MJ fields\n const deserializedResults: T[] = result.Results.map(r => {\n const data = JSON.parse(r.Data);\n this.ConvertBackToMJFields(data);\n return data as T;\n });\n\n return {\n viewIndex: result.viewIndex,\n status: result.status as 'current' | 'stale' | 'differential' | 'error',\n results: deserializedResults,\n maxUpdatedAt: result.maxUpdatedAt,\n rowCount: result.rowCount,\n errorMessage: result.errorMessage,\n };\n }\n\n return {\n viewIndex: result.viewIndex,\n status: result.status as 'current' | 'stale' | 'differential' | 'error',\n results: undefined,\n maxUpdatedAt: result.maxUpdatedAt,\n rowCount: result.rowCount,\n errorMessage: result.errorMessage,\n };\n });\n\n return {\n success: response.success,\n results: transformedResults,\n errorMessage: response.errorMessage,\n };\n } catch (e) {\n LogError(e);\n return {\n success: false,\n results: [],\n errorMessage: e instanceof Error ? e.message : String(e),\n };\n }\n }\n\n protected async getEntityNameAndUserView(params: RunViewParams, contextUser?: UserInfo): Promise<{entityName: string, v: MJUserViewEntityExtended}> {\n let entityName: string;\n let v: MJUserViewEntityExtended;\n\n if (!params.EntityName) {\n if (params.ViewID) {\n v = await ViewInfo.GetViewEntity(params.ViewID, contextUser)\n entityName = v.Entity\n }\n else if (params.ViewName) {\n v = await ViewInfo.GetViewEntityByName(params.ViewName, contextUser);\n entityName = v.Entity\n }\n else\n throw new Error(`No EntityName, ViewID or ViewName passed to RunView`)\n }\n else\n entityName = params.EntityName\n\n return {entityName, v}\n }\n\n protected getViewRunTimeFieldList(e: EntityInfo, v: MJUserViewEntityExtended, params: RunViewParams, dynamicView: boolean): string[] {\n const fieldList = [];\n const mapper = new FieldMapper();\n if (params.Fields) {\n for (const kv of e.PrimaryKeys) {\n if (params.Fields.find(f => f.trim().toLowerCase() === kv.Name.toLowerCase()) === undefined)\n fieldList.push(kv.Name); // always include the primary key fields in view run time field list\n }\n\n // now add any other fields that were passed in\n params.Fields.forEach(f => {\n fieldList.push(mapper.MapFieldName(f))\n });\n }\n else {\n // no fields were passed in. So, let's check to see if we are running an dynamic view.\n // If so, we need to include all fields since the caller didn't specify the fields they want\n // otherwise, we include the fields that are part of the view definition.\n if (dynamicView) {\n // include all fields since no fields were passed in\n e.Fields.forEach(f => {\n if (!f.IsBinaryFieldType) {\n fieldList.push(mapper.MapFieldName(f.CodeName));\n }\n });\n }\n else {\n // NOTE: in the below, c.EntityField SHOULD always exist, however there is a possibility that at some point a VIEW was created that used fields\n // and those fields are NO LONGER part of an entity, in that situation we should just remove them, rather than letting the whole view blow up which\n // would happen if we dno't check for c.EntityField? in the below\n\n // first make sure we have the primary key field in the view column list, always should, but make sure\n for (const kv of e.PrimaryKeys) {\n if (fieldList.find(f => f.trim().toLowerCase() === kv.Name.toLowerCase()) === undefined)\n fieldList.push(kv.Name); // always include the primary key fields in view run time field list\n }\n\n // Now: include the fields that are part of the view definition\n v.Columns.forEach(c => {\n if (c.hidden === false && !fieldList.find(item => item.trim().toLowerCase() === c.EntityField?.Name.trim().toLowerCase())) { // don't include hidden fields and don't include the pkey field again\n if (!c.EntityField) {\n // this can happen if a field was previously included in a view, but is no longer part of the entity\n // simply don't include it in the field list\n }\n else\n fieldList.push(mapper.MapFieldName(c.EntityField.CodeName));\n }\n });\n }\n }\n return fieldList;\n }\n /**************************************************************************/\n // END ---- IRunViewProvider\n /**************************************************************************/\n\n\n /**************************************************************************/\n // START ---- IEntityDataProvider\n /**************************************************************************/\n public get ProviderType(): ProviderType {\n return ProviderType.Network;\n }\n\n public async GetRecordChanges(entityName: string, primaryKey: CompositeKey): Promise<RecordChange[]> {\n try {\n const p: RunViewParams = {\n EntityName: 'MJ: Record Changes',\n ExtraFilter: `RecordID = '${primaryKey.Values()}' AND Entity = '${entityName}'`,\n //OrderBy: 'ChangedAt DESC',\n }\n const result = await this.RunView(p);\n if (result) {\n // sort the results client side because, for now, the RunViewParams doesn't support OrderBy dynamically like we tried. Later change this to do via the SQL query\n return result.Results.sort((a: RecordChange, b: RecordChange) => {\n return (a.ChangedAt > b.ChangedAt) ? -1 : 1 // sort descending on the date.... GraphQL passes back the date as time since base date\n });\n }\n else\n return null;\n }\n catch (e) {\n LogError(e);\n throw (e)\n }\n }\n\n\n /**\n * Returns a list of dependencies - records that are linked to the specified Entity/KeyValuePairs combination. A dependency is as defined by the relationships in the database. The MemberJunction metadata that is used\n * for this simply reflects the foreign key relationships that exist in the database. The CodeGen tool is what detects all of the relationships and generates the metadata that is used by MemberJunction. The metadata in question\n * is within the EntityField table and specifically the RelatedEntity and RelatedEntityField columns. In turn, this method uses that metadata and queries the database to determine the dependencies. To get the list of entity dependencies\n * you can use the utility method GetEntityDependencies(), which doesn't check for dependencies on a specific record, but rather gets the metadata in one shot that can be used for dependency checking.\n * @param entityName the name of the entity to check\n * @param KeyValuePairs the KeyValuePairs of the record to check\n */\n public async GetRecordDependencies(entityName: string, primaryKey: CompositeKey): Promise<RecordDependency[]> {\n try {\n // execute the gql query to get the dependencies\n const query = gql`query GetRecordDependenciesQuery ($entityName: String!, $CompositeKey: CompositeKeyInputType!) {\n GetRecordDependencies(entityName: $entityName, CompositeKey: $CompositeKey) {\n EntityName\n RelatedEntityName\n FieldName\n CompositeKey {\n KeyValuePairs {\n FieldName\n Value\n }\n }\n }\n }`\n\n // now we have our query built, execute it\n const vars = {\n entityName: entityName,\n CompositeKey: {KeyValuePairs: this.ensureKeyValuePairValueIsString(primaryKey.KeyValuePairs)}\n };\n const data = await this.ExecuteGQL(query, vars);\n\n return data?.GetRecordDependencies; // shape of the result should exactly match the RecordDependency type\n }\n catch (e) {\n LogError(e);\n throw (e)\n }\n }\n\n protected ensureKeyValuePairValueIsString(kvps: KeyValuePair[]): {FieldName: string, Value: string}[] {\n return kvps.map(kv => {\n return {FieldName: kv.FieldName, Value: kv.Value.toString()}\n })\n }\n\n public async GetRecordDuplicates(params: PotentialDuplicateRequest, contextUser?: UserInfo): Promise<PotentialDuplicateResponse>\n {\n if(!params){\n return null;\n }\n\n const query: string = gql`query GetRecordDuplicatesQuery ($params: PotentialDuplicateRequestType!) {\n GetRecordDuplicates(params: $params) {\n Status\n ErrorMessage\n PotentialDuplicateResult {\n EntityID\n DuplicateRunDetailMatchRecordIDs\n RecordPrimaryKeys {\n KeyValuePairs {\n FieldName\n Value\n }\n }\n Duplicates {\n ProbabilityScore\n KeyValuePairs {\n FieldName\n Value\n }\n }\n }\n }\n }`\n\n let request = {\n EntityID: params.EntityID,\n EntityDocumentID: params.EntityDocumentID,\n ListID: params.ListID,\n ProbabilityScore: params.ProbabilityScore,\n Options: params.Options,\n RecordIDs: params.RecordIDs.map(recordID => {\n return recordID.Copy();\n })\n }\n const data = await this.ExecuteGQL(query, {params: request});\n\n if(data && data.GetRecordDuplicates){\n return data.GetRecordDuplicates;\n }\n }\n\n public async MergeRecords(request: RecordMergeRequest, contextUser?: UserInfo, options?: EntityMergeOptions): Promise<RecordMergeResult> {\n const e = this.Entities.find(e=>e.Name.trim().toLowerCase() === request.EntityName.trim().toLowerCase());\n if (!e || !e.AllowRecordMerge)\n throw new Error(`Entity ${request.EntityName} does not allow record merging, check the AllowRecordMerge property in the entity metadata`);\n\n try {\n // execute the gql query to get the dependencies\n const mutation = gql`mutation MergeRecordsMutation ($request: RecordMergeRequest!) {\n MergeRecords(request: $request) {\n Success\n OverallStatus\n RecordMergeLogID\n RecordStatus {\n CompositeKey {\n KeyValuePairs {\n FieldName\n Value\n }\n }\n Success\n RecordMergeDeletionLogID\n Message\n }\n }\n }`\n\n // create a new request that is compatible with the server's expectations where field maps and also the primary key values are all strings\n const newRequest = {\n EntityName: request.EntityName,\n SurvivingRecordCompositeKey: {KeyValuePairs: this.ensureKeyValuePairValueIsString(request.SurvivingRecordCompositeKey.KeyValuePairs)},\n FieldMap: request.FieldMap?.map(fm => {\n return {\n FieldName: fm.FieldName,\n Value: fm.Value.toString() // turn the value into a string, since that is what the server expects\n }\n }),\n RecordsToMerge: request.RecordsToMerge.map(r => {\n return r.Copy();\n })\n }\n\n // now we have our query built, execute it\n const data = await this.ExecuteGQL(mutation, {request: newRequest});\n\n return data?.MergeRecords; // shape of the result should exactly match the RecordDependency type\n }\n catch (e) {\n LogError(e);\n return {\n Success: false,\n OverallStatus: e && e.message ? e.message : e,\n RecordStatus: [],\n RecordMergeLogID: \"\",\n Request: request,\n }\n throw (e)\n }\n }\n\n public async Save(entity: BaseEntity, user: UserInfo, options: EntitySaveOptions) : Promise<{}> {\n // IS-A parent entity save: the full ORM pipeline (permissions, validation, events)\n // already ran in BaseEntity._InnerSave(). Skip the network call — the leaf entity's\n // mutation will include all chain fields. Return current entity state.\n if (options?.IsParentEntitySave) {\n const result = new BaseEntityResult();\n result.StartedAt = new Date();\n result.EndedAt = new Date();\n result.Type = entity.IsSaved ? 'update' : 'create';\n result.Success = true;\n result.NewValues = entity.GetAll();\n entity.ResultHistory.push(result);\n return result.NewValues;\n }\n\n const result = new BaseEntityResult();\n try {\n entity.RegisterTransactionPreprocessing(); // as of the time of writing, this isn't technically needed because we are not doing any async preprocessing, but it is good to have it here for future use in case something is added with async between here and the TransactionItem being added.\n\n const vars = { input: {} };\n const type: string = entity.IsSaved ? \"Update\" : \"Create\";\n\n result.StartedAt = new Date();\n result.Type = entity.IsSaved ? 'update' : 'create';\n result.OriginalValues = entity.Fields.map(f => { return {FieldName: f.CodeName, Value: f.Value} });\n entity.ResultHistory.push(result); // push the new result as we have started a process\n\n // Create the query for the mutation first, we will provide the specific\n // input values later in the loop below. Here we are just setting up the mutation\n // and the fields that will be returned since the mutation returns back the latest\n // values for the entity and we need to have those values to update the entity after the\n // save\n\n const graphQLTypeName = getGraphQLTypeNameBase(entity.EntityInfo);\n const mutationName = `${type}${graphQLTypeName}`\n\n // only pass along writable fields, AND the PKEY value if this is an update\n const filteredFields = entity.Fields.filter(f => !f.ReadOnly || (f.IsPrimaryKey && entity.IsSaved));\n const mapper = new FieldMapper();\n const inner = ` ${mutationName}(input: $input) {\n ${entity.Fields.map(f => mapper.MapFieldName(f.CodeName)).join(\"\\n \")}\n }`\n const outer = gql`mutation ${type}${graphQLTypeName} ($input: ${mutationName}Input!) {\n ${inner}\n }\n `\n for (let i = 0; i < filteredFields.length; i++) {\n const f = filteredFields[i];\n // use entity.Get() instead of f.Value\n // in case there is an IsA relationship where parent entity \n // is where the value is. f.Value would still be old value\n let val = entity.Get(f.Name); \n if (val) {\n // type conversions as needed for GraphQL\n switch(f.EntityFieldInfo.TSType) {\n case EntityFieldTSType.Date:\n val = val.getTime();\n break;\n case EntityFieldTSType.Boolean:\n if (typeof val !== 'boolean') {\n val = parseInt(val) === 0 ? false : true; // convert to boolean\n }\n break;\n case EntityFieldTSType.Number:\n if (typeof val !== 'number') {\n const numValue = Number(val);\n if (!isNaN(numValue)) {\n val = numValue;\n } \n }\n break;\n }\n }\n\n if (val === null && f.EntityFieldInfo.AllowsNull === false) {\n // no value, field doesn't allow nulls, so set to default value, if available and then fall back to either 0 or empty string depending on type\n if (f.EntityFieldInfo.DefaultValue !== null) {\n // no value, but there is a default value, so use that, since field does NOT allow NULL\n val = f.EntityFieldInfo.DefaultValue;\n }\n else {\n // no default value, null value and field doesn't allow nulls, so set to either 0 or empty string\n if (f.FieldType === EntityFieldTSType.Number || f.FieldType === EntityFieldTSType.Boolean)\n val = 0;\n else\n val = '';\n }\n }\n vars.input[f.CodeName] = val;\n }\n\n // now add an OldValues prop to the vars IF the type === 'update' and the options.SkipOldValuesCheck === false\n if (type.trim().toLowerCase() === 'update' &&\n options.SkipOldValuesCheck === false) {\n const ov = [];\n entity.Fields.forEach(f => {\n let val = null;\n if (f.OldValue !== null && f.OldValue !== undefined) {\n if (f.EntityFieldInfo.TSType === EntityFieldTSType.Date) \n val = f.OldValue.getTime().toString();\n else if (f.EntityFieldInfo.TSType === EntityFieldTSType.Boolean)\n val = f.OldValue === true ? \"1\" : \"0\";\n else if (typeof f.OldValue !== \"string\")\n val = f.OldValue.toString();\n else\n val = f.OldValue;\n }\n ov.push({Key: f.CodeName, Value: val }); // pass ALL old values to server, slightly inefficient but we want full record\n });\n vars.input['OldValues___'] = ov; // add the OldValues prop to the input property that is part of the vars already\n }\n\n if (entity.TransactionGroup) {\n const mutationInputTypes = [\n {\n varName: 'input',\n inputType: mutationName + 'Input!'\n }\n ];\n\n entity.RaiseReadyForTransaction(); // let the entity know we're ready to be part of the transaction\n\n // we are part of a transaction group, so just add our query to the list\n // and when the transaction is committed, we will send all the queries at once\n entity.TransactionGroup.AddTransaction(new TransactionItem( entity, \n result.Type === 'create' ? 'Create' : 'Update', \n inner, vars, \n {\n mutationName,\n mutationInputTypes: mutationInputTypes\n },\n (results: any, success: boolean) => {\n // we get here whenever the transaction group does gets around to committing\n // our query. We need to update our entity with the values that were returned\n // from the mutation if it was successful.\n result.EndedAt = new Date();\n if (success && results) {\n // got our data, send it back to the caller, which is the entity object\n // and that object needs to update itself from this data.\n result.Success = true;\n result.NewValues = this.ConvertBackToMJFields(results);\n }\n else {\n // the transaction failed, nothing to update, but we need to call Reject so the\n // promise resolves with a rejection so our outer caller knows\n result.Success = false;\n result.Message = 'Transaction failed';\n }\n }));\n\n return true; // part of a TG always return true after we setup the transaction group item above\n }\n else {\n // not part of a transaction group, so just go for it and send across our GQL\n const d = await this.ExecuteGQL(outer, vars)\n if (d && d[mutationName]) {\n result.Success = true;\n result.EndedAt = new Date();\n result.NewValues = this.ConvertBackToMJFields(d[mutationName]);\n return result.NewValues;\n }\n else\n throw new Error(`Save failed for ${entity.EntityInfo.ClassName}`);\n }\n }\n catch (e) {\n result.Success = false;\n result.EndedAt = new Date();\n result.Message = e.response?.errors?.length > 0 ? e.response.errors[0].message : e.message;\n LogError(e);\n return null;\n }\n }\n public async Load(entity: BaseEntity, primaryKey: CompositeKey, EntityRelationshipsToLoad: string[] = null, user: UserInfo) : Promise<{}> {\n try {\n const vars = {};\n let pkeyInnerParamString: string = '';\n let pkeyOuterParamString: string = '';\n\n for (let i = 0; i < primaryKey.KeyValuePairs.length; i++) {\n const field: EntityFieldInfo = entity.Fields.find(f => f.Name.trim().toLowerCase() === primaryKey.KeyValuePairs[i].FieldName.trim().toLowerCase()).EntityFieldInfo;\n const val = primaryKey.GetValueByIndex(i);\n const pkeyGraphQLType: string = field.GraphQLType;\n\n // build up the param string for the outer query definition\n if (pkeyOuterParamString.length > 0)\n pkeyOuterParamString += ', ';\n pkeyOuterParamString += `$${field.CodeName}: ${pkeyGraphQLType}!`;\n\n // build up the param string for the inner query call\n if (pkeyInnerParamString.length > 0)\n pkeyInnerParamString += ', ';\n pkeyInnerParamString += `${field.CodeName}: $${field.CodeName}`;\n\n // build up the variables we are passing along to the query\n if (field.TSType === EntityFieldTSType.Number) {\n if (isNaN(primaryKey.GetValueByIndex(i)))\n throw new Error(`Primary Key value ${val} (${field.Name}) is not a valid number`);\n vars[field.CodeName] = parseInt(val); // converting to number here for graphql type to work properly\n }\n else\n vars[field.CodeName] = val;\n }\n\n const rel = EntityRelationshipsToLoad && EntityRelationshipsToLoad.length > 0 ? this.getRelatedEntityString(entity.EntityInfo, EntityRelationshipsToLoad) : '';\n\n const graphQLTypeName = getGraphQLTypeNameBase(entity.EntityInfo);\n const mapper = new FieldMapper();\n const query = gql`query Single${graphQLTypeName}${rel.length > 0 ? 'Full' : ''} (${pkeyOuterParamString}) {\n ${graphQLTypeName}(${pkeyInnerParamString}) {\n ${entity.Fields.filter((f) => !f.EntityFieldInfo.IsBinaryFieldType)\n .map((f) => {\n if (f.EntityFieldInfo.Name.trim().toLowerCase().startsWith('__mj_')) {\n // fields that start with __mj_ need to be converted to _mj__ for the GraphQL query\n return f.CodeName.replace('__mj_', '_mj__');\n } else {\n return f.CodeName;\n }\n })\n .join('\\n ')}\n ${rel}\n }\n }\n `;\n\n const d = await this.ExecuteGQL(query, vars)\n if (d && d[graphQLTypeName]) {\n // the resulting object has all the values in it, but we need to convert any elements that start with _mj__ back to __mj_\n return this.ConvertBackToMJFields(d[graphQLTypeName]);\n }\n else\n return null;\n }\n catch (e) {\n LogError(e);\n return null;\n }\n }\n\n /**\n * This method will convert back any fields that start with _mj__ back to __mj_ so that the entity object can properly update itself with the data that was returned from the server\n * @param ret\n * @returns\n */\n protected ConvertBackToMJFields(ret: any): any {\n const mapper = new FieldMapper();\n mapper.ReverseMapFields(ret);\n return ret; // clean object to pass back here\n }\n\n protected getRelatedEntityString(entityInfo: EntityInfo, EntityRelationshipsToLoad: string[]): string {\n let rel = '';\n for (let i = 0; i < entityInfo.RelatedEntities.length; i++) {\n if (EntityRelationshipsToLoad.indexOf(entityInfo.RelatedEntities[i].RelatedEntity) >= 0) {\n const r = entityInfo.RelatedEntities[i];\n const re = this.Entities.find(e => e.ID === r.RelatedEntityID);\n let uniqueCodeName: string = '';\n if (r.Type.toLowerCase().trim() === 'many to many') {\n uniqueCodeName = `${r.RelatedEntityCodeName}_${r.JoinEntityJoinField.replace(/\\s/g, '')}`;\n }\n else {\n uniqueCodeName = `${r.RelatedEntityCodeName}_${r.RelatedEntityJoinField.replace(/\\s/g, '')}`;\n }\n rel += `\n ${uniqueCodeName} {\n ${re.Fields.map(f => f.CodeName).join(\"\\n \")}\n }\n `;\n }\n }\n return rel;\n }\n\n public async Delete(entity: BaseEntity, options: EntityDeleteOptions, user: UserInfo) : Promise<boolean> {\n const result = new BaseEntityResult();\n try {\n entity.RegisterTransactionPreprocessing();\n\n result.StartedAt = new Date();\n result.Type = 'delete';\n result.OriginalValues = entity.Fields.map(f => { return {FieldName: f.CodeName, Value: f.Value} });\n entity.ResultHistory.push(result); // push the new result as we have started a process\n\n const vars = {};\n const mutationInputTypes = [];\n let pkeyInnerParamString: string = '';\n let pkeyOuterParamString: string = '';\n let returnValues: string = '';\n for (let kv of entity.PrimaryKey.KeyValuePairs) {\n const pk = entity.Fields.find(f => f.Name.trim().toLowerCase() === kv.FieldName.trim().toLowerCase()); // get the field for the primary key field\n vars[pk.CodeName] = pk.Value;\n mutationInputTypes.push({varName: pk.CodeName, inputType: pk.EntityFieldInfo.GraphQLType + '!'}); // only used when doing a transaction group, but it is easier to do in this main loop\n if (pkeyInnerParamString.length > 0)\n pkeyInnerParamString += ', ';\n pkeyInnerParamString += `${pk.CodeName}: $${pk.CodeName}`;\n\n if (pkeyOuterParamString.length > 0)\n pkeyOuterParamString += ', ';\n pkeyOuterParamString += `$${pk.CodeName}: ${pk.EntityFieldInfo.GraphQLType}!`;\n\n if (returnValues.length > 0)\n returnValues += '\\n ';\n returnValues += `${pk.CodeName}`;\n }\n\n mutationInputTypes.push({varName: \"options___\", inputType: 'DeleteOptionsInput!'}); // only used when doing a transaction group, but it is easier to do in this main loop\n\n // Build delete options ensuring all required fields are present.\n // IMPORTANT: Must be kept in sync with DeleteOptionsInput in @memberjunction/server\n // and EntityDeleteOptions in @memberjunction/core\n vars[\"options___\"] = {\n SkipEntityAIActions: options?.SkipEntityAIActions ?? false,\n SkipEntityActions: options?.SkipEntityActions ?? false,\n ReplayOnly: options?.ReplayOnly ?? false,\n IsParentEntityDelete: options?.IsParentEntityDelete ?? false\n };\n\n const graphQLTypeName = getGraphQLTypeNameBase(entity.EntityInfo);\n const queryName: string = 'Delete' + graphQLTypeName;\n const inner = gql`${queryName}(${pkeyInnerParamString}, options___: $options___) {\n ${returnValues}\n }\n `\n const query = gql`mutation ${queryName} (${pkeyOuterParamString}, $options___: DeleteOptionsInput!) {\n ${inner}\n }\n `\n\n if (entity.TransactionGroup) {\n // we have a transaction group, need to play nice and be part of it\n entity.RaiseReadyForTransaction();\n // we are part of a transaction group, so just add our query to the list\n // and when the transaction is committed, we will send all the queries at once\n entity.TransactionGroup.AddTransaction(new TransactionItem(entity, 'Delete', inner, vars, {mutationName: queryName,\n mutationInputTypes: mutationInputTypes},\n (results: any, success: boolean) => {\n // we get here whenever the transaction group does gets around to committing\n // our query.\n result.EndedAt = new Date(); // done processing\n if (success && results) {\n // success indicated by the entity.PrimaryKey.Value matching the return value of the mutation\n let success: boolean = true;\n for (const pk of entity.PrimaryKey.KeyValuePairs) {\n // check each primary key value to see if it matches the return value of the mutation\n if (pk.Value !== results[pk.FieldName]) {\n success = false;\n }\n }\n if (success) {\n result.Success = true;\n }\n else {\n // the transaction failed, nothing to update, but we need to call Reject so the\n // promise resolves with a rejection so our outer caller knows\n result.Success = false;\n result.Message = 'Transaction failed to commit'\n }\n }\n else {\n // the transaction failed, nothing to update, but we need to call Reject so the\n // promise resolves with a rejection so our outer caller knows\n result.Success = false;\n result.Message = 'Transaction failed to commit'\n }\n }));\n return true;\n }\n else {\n // no transaction just go for it\n const d = await this.ExecuteGQL(query, vars)\n if (d && d[queryName]) {\n const data = d[queryName];\n for (let key of entity.PrimaryKey.KeyValuePairs) {\n // we want to now compare key.Value against data[key.FieldName]\n let returnedVal = data[key.FieldName];\n let originalVal = key.Value;\n // we want to ignore types so we should convert numbers to strings for the comparison\n if (typeof originalVal === 'number')\n originalVal = originalVal.toString();\n if (typeof returnedVal === 'number')\n returnedVal = returnedVal.toString();\n // now compare the two values\n if (originalVal !== returnedVal) {\n throw new Error (`Primary key value mismatch in server Delete response. Field: ${key.FieldName}, Original: ${originalVal}, Returned: ${returnedVal}`);\n }\n }\n result.Success = true;\n result.EndedAt = new Date(); // done processing\n return true; // all of the return values match the primary key values, so we are good and delete worked\n }\n else\n throw new Error(`Delete failed for ${entity.EntityInfo.Name}: ${entity.PrimaryKey.ToString()} `);\n }\n }\n catch (e) {\n result.EndedAt = new Date(); // done processing\n result.Success = false;\n result.Message = e.response?.errors?.length > 0 ? e.response.errors[0].message : e.message;\n LogError(e);\n\n return false;\n }\n }\n /**************************************************************************/\n // END ---- IEntityDataProvider\n /**************************************************************************/\n\n\n /**************************************************************************/\n // START ---- IMetadataProvider\n /**************************************************************************/\n /**\n * Returns a dataset by name\n * @param datasetName \n * @param itemFilters \n * @returns \n */\n public async GetDatasetByName(datasetName: string, itemFilters?: DatasetItemFilterType[]): Promise<DatasetResultType> {\n const query = gql`query GetDatasetByName($DatasetName: String!, $ItemFilters: [DatasetItemFilterTypeGQL!]) {\n GetDatasetByName(DatasetName: $DatasetName, ItemFilters: $ItemFilters) {\n DatasetID\n DatasetName\n Success\n Status\n LatestUpdateDate\n Results\n }\n }`\n const data = await this.ExecuteGQL(query, {DatasetName: datasetName, ItemFilters: itemFilters });\n if (data && data.GetDatasetByName && data.GetDatasetByName.Success) {\n return {\n DatasetID: data.GetDatasetByName.DatasetID,\n DatasetName: data.GetDatasetByName.DatasetName,\n Success: data.GetDatasetByName.Success,\n Status: data.GetDatasetByName.Status,\n LatestUpdateDate: new Date(data.GetDatasetByName.LatestUpdateDate),\n Results: JSON.parse(data.GetDatasetByName.Results)\n }\n }\n else {\n return {\n DatasetID: \"\",\n DatasetName: datasetName,\n Success: false,\n Status: 'Unknown',\n LatestUpdateDate: null,\n Results: null\n };\n }\n }\n\n public async GetDatasetStatusByName(datasetName: string, itemFilters?: DatasetItemFilterType[]): Promise<DatasetStatusResultType> {\n const query = gql`query GetDatasetStatusByName($DatasetName: String!, $ItemFilters: [DatasetItemFilterTypeGQL!]) {\n GetDatasetStatusByName(DatasetName: $DatasetName, ItemFilters: $ItemFilters) {\n DatasetID\n DatasetName\n Success\n Status\n LatestUpdateDate\n EntityUpdateDates\n }\n }`\n const data = await this.ExecuteGQL(query, {DatasetName: datasetName, ItemFilters: itemFilters});\n if (data && data.GetDatasetStatusByName && data.GetDatasetStatusByName.Success) {\n return {\n DatasetID: data.GetDatasetStatusByName.DatasetID,\n DatasetName: data.GetDatasetStatusByName.DatasetName,\n Success: data.GetDatasetStatusByName.Success,\n Status: data.GetDatasetStatusByName.Status,\n LatestUpdateDate: new Date(data.GetDatasetStatusByName.LatestUpdateDate),\n EntityUpdateDates: JSON.parse(data.GetDatasetStatusByName.EntityUpdateDates)\n }\n }\n else {\n return {\n DatasetID: \"\",\n DatasetName: datasetName,\n Success: false,\n Status: 'Unknown',\n LatestUpdateDate: null,\n EntityUpdateDates: null\n };\n }\n }\n\n public async CreateTransactionGroup(): Promise<TransactionGroupBase> {\n return new GraphQLTransactionGroup(this);\n }\n\n public async GetRecordFavoriteStatus(userId: string, entityName: string, primaryKey: CompositeKey): Promise<boolean> {\n const valResult = primaryKey.Validate();\n if (!valResult.IsValid)\n return false;\n\n const e = this.Entities.find(e => e.Name === entityName)\n if (!e)\n throw new Error(`Entity ${entityName} not found in metadata`);\n\n const query = gql`query GetRecordFavoriteStatus($params: UserFavoriteSearchParams!) {\n GetRecordFavoriteStatus(params: $params) {\n Success\n IsFavorite\n }\n }`\n\n const data = await this.ExecuteGQL(query, {params: {\n UserID: userId,\n EntityID: e.ID,\n CompositeKey: {KeyValuePairs: this.ensureKeyValuePairValueIsString(primaryKey.KeyValuePairs)}\n }\n }\n );\n if (data && data.GetRecordFavoriteStatus && data.GetRecordFavoriteStatus.Success)\n return data.GetRecordFavoriteStatus.IsFavorite;\n }\n\n public async SetRecordFavoriteStatus(userId: string, entityName: string, primaryKey: CompositeKey, isFavorite: boolean, contextUser: UserInfo): Promise<void> {\n const e = this.Entities.find(e => e.Name === entityName)\n if (!e){\n throw new Error(`Entity ${entityName} not found in metadata`);\n }\n\n const query = gql`mutation SetRecordFavoriteStatus($params: UserFavoriteSetParams!) {\n SetRecordFavoriteStatus(params: $params){\n Success\n }\n }`\n\n const data = await this.ExecuteGQL(query, { params: {\n UserID: userId,\n EntityID: e.ID,\n CompositeKey: {KeyValuePairs: this.ensureKeyValuePairValueIsString(primaryKey.KeyValuePairs)},\n IsFavorite: isFavorite}\n }\n );\n if (data && data.SetRecordFavoriteStatus !== null)\n return data.SetRecordFavoriteStatus.Success;\n }\n\n protected async InternalGetEntityRecordName(entityName: string, primaryKey: CompositeKey): Promise<string> {\n if (!entityName || !primaryKey || primaryKey.KeyValuePairs?.length === 0){\n return null;\n }\n\n const query = gql`query GetEntityRecordNameQuery ($EntityName: String!, $CompositeKey: CompositeKeyInputType!) {\n GetEntityRecordName(EntityName: $EntityName, CompositeKey: $CompositeKey) {\n Success\n Status\n RecordName\n }\n }`\n\n const data = await this.ExecuteGQL(query, {\n EntityName: entityName,\n CompositeKey: {KeyValuePairs: this.ensureKeyValuePairValueIsString(primaryKey.KeyValuePairs)}\n });\n if (data && data.GetEntityRecordName && data.GetEntityRecordName.Success)\n return data.GetEntityRecordName.RecordName;\n }\n\n protected async InternalGetEntityRecordNames(info: EntityRecordNameInput[]): Promise<EntityRecordNameResult[]> {\n if (!info)\n return null;\n\n const query = gql`query GetEntityRecordNamesQuery ($info: [EntityRecordNameInput!]!) {\n GetEntityRecordNames(info: $info) {\n Success\n Status\n CompositeKey {\n KeyValuePairs {\n FieldName\n Value\n }\n }\n EntityName\n RecordName\n }\n }`\n\n const data = await this.ExecuteGQL(query, {info: info.map(i => {\n return {\n EntityName: i.EntityName,\n CompositeKey: {KeyValuePairs: this.ensureKeyValuePairValueIsString(i.CompositeKey.KeyValuePairs)}\n }\n })});\n if (data && data.GetEntityRecordNames) {\n // Convert plain CompositeKey objects from GraphQL response to real CompositeKey instances\n return data.GetEntityRecordNames.map((result: EntityRecordNameResult) => ({\n ...result,\n CompositeKey: new CompositeKey(result.CompositeKey.KeyValuePairs)\n }));\n }\n }\n\n /**\n * Retrieves all of the data context data for the specified data context ID.\n * @param dataContextID \n * @returns \n */\n public async GetDataContextData(dataContextID: string) {\n try {\n const query = gql`query GetDataContextData ($DataContextID: String!) {\n GetDataContextData(DataContextID: $DataContextID) {\n Success\n ErrorMessages\n Results\n }\n }`\n \n const data = await this.ExecuteGQL(query, {DataContextID: dataContextID});\n if (data && data.GetDataContextData) {\n if (data.GetDataContextData.Success) {\n return data.GetDataContextData.Results.map((item: any) => {\n return JSON.parse(item);\n });\n }\n else {\n throw new Error(data.GetDataContextData.ErrorMessages.join(', '));\n }\n }\n else {\n throw new Error('GraphQL query failed');\n } \n }\n catch (e) {\n LogError(e);\n throw e;\n }\n }\n\n /**\n * Retrieves the data context item data for the specified data context item ID.\n * @param dataContextItemID \n * @returns \n */\n public async GetDataContextItemData(dataContextItemID: string) {\n try {\n const query = gql`query GetDataContextItemData ($DataContextItemID: String!) {\n GetDataContextItemData(DataContextItemID: $DataContextItemID) {\n Success\n ErrorMessage\n Result\n }\n }`\n \n const data = await this.ExecuteGQL(query, {DataContextItemID: dataContextItemID});\n if (data && data.GetDataContextItemData) {\n if (data.GetDataContextItemData.Success) {\n return JSON.parse(data.GetDataContextItemData.Result);\n }\n else {\n throw new Error(data.GetDataContextItemData.ErrorMessage);\n }\n }\n else {\n throw new Error('GraphQL query failed');\n } \n }\n catch (e) {\n LogError(e);\n throw e;\n }\n }\n\n /**\n * Static version of the ExecuteGQL method that will use the global instance of the GraphQLDataProvider and execute the specified query with the provided variables. \n * If the token is expired, it will attempt to refresh the token and then re-execute the query. If the token is expired and the refresh fails, it will throw an error.\n * @param query \n * @param variables \n * @param refreshTokenIfNeeded \n * @returns \n */\n public static async ExecuteGQL(query: string, variables: any, refreshTokenIfNeeded: boolean = true): Promise<any> {\n return GraphQLDataProvider.Instance.ExecuteGQL(query, variables, refreshTokenIfNeeded);\n }\n\n /**\n * Executes the GQL query with the provided variables. If the token is expired, it will attempt to refresh the token and then re-execute the query. If the token is expired and the refresh fails, it will throw an error.\n * @param query \n * @param variables \n * @param refreshTokenIfNeeded \n * @returns \n */\n public async ExecuteGQL(query: string, variables: any, refreshTokenIfNeeded: boolean = true): Promise<any> {\n try {\n const data = await this._client.request(query, variables);\n return data;\n }\n catch (e) {\n // Enhanced error logging to diagnose 500 errors\n console.error('[GraphQL] ExecuteGQL error caught:', {\n hasResponse: !!e?.response,\n hasErrors: !!e?.response?.errors,\n errorCount: e?.response?.errors?.length,\n firstError: e?.response?.errors?.[0],\n errorCode: e?.response?.errors?.[0]?.extensions?.code,\n errorMessage: e?.response?.errors?.[0]?.message,\n fullError: e\n });\n\n if (e && e.response && e.response.errors?.length > 0) {//e.code === 'JWT_EXPIRED') {\n const error = e.response.errors[0];\n const code = error?.extensions?.code?.toUpperCase().trim()\n if (code === 'JWT_EXPIRED') {\n if (refreshTokenIfNeeded) {\n // token expired, so we need to refresh it and try again\n await this.RefreshToken();\n return await this.ExecuteGQL(query, variables, false/*don't attempt to refresh again*/);\n }\n else {\n // token expired but the caller doesn't want a refresh, so just return the error\n LogError(`JWT_EXPIRED and refreshTokenIfNeeded is false`);\n throw e;\n }\n }\n else\n throw e;\n }\n else {\n LogError(e);\n throw e; // force the caller to handle the error\n }\n }\n }\n\n public async RefreshToken(): Promise<void> {\n // Check if we're in singleton mode\n const isInstanceSingleton = GraphQLDataProvider.Instance &&\n GraphQLDataProvider.Instance._configData === this._configData;\n\n // If singleton has a refresh in progress, wait for it\n if (isInstanceSingleton && GraphQLDataProvider.Instance._refreshPromise) {\n return GraphQLDataProvider.Instance._refreshPromise;\n }\n\n // If this instance has a refresh in progress, wait for it\n if (this._refreshPromise) {\n return this._refreshPromise;\n }\n\n // Start a new refresh\n console.log('[GraphQL] Starting token refresh...');\n this._refreshPromise = this.performTokenRefresh();\n\n // Also store on singleton if in singleton mode\n if (isInstanceSingleton) {\n GraphQLDataProvider.Instance._refreshPromise = this._refreshPromise;\n }\n\n try {\n await this._refreshPromise;\n console.log('[GraphQL] Token refresh completed successfully');\n } finally {\n // Clear the promise when done\n this._refreshPromise = null;\n if (isInstanceSingleton && GraphQLDataProvider.Instance) {\n GraphQLDataProvider.Instance._refreshPromise = null;\n }\n }\n }\n\n private async performTokenRefresh(): Promise<void> {\n if (this._configData.Data.RefreshTokenFunction) {\n const newToken = await this._configData.Data.RefreshTokenFunction();\n if (newToken) {\n this._configData.Token = newToken; // update the token\n const newClient = this.CreateNewGraphQLClient(this._configData.URL,\n this._configData.Token,\n this._sessionId,\n this._configData.MJAPIKey,\n this._configData.UserAPIKey);\n\n // Update this instance's client\n this._client = newClient;\n\n // CRITICAL: Also update the singleton's client if we're in singleton mode\n // Check if this._configData is the same reference as Instance._configData\n if (GraphQLDataProvider.Instance &&\n GraphQLDataProvider.Instance._configData === this._configData) {\n GraphQLDataProvider.Instance._client = newClient;\n }\n }\n else {\n throw new Error('Refresh token function returned null or undefined token');\n }\n }\n else {\n throw new Error('No refresh token function provided');\n }\n }\n\n public static async RefreshToken(): Promise<void> {\n return GraphQLDataProvider.Instance.RefreshToken();\n }\n\n protected CreateNewGraphQLClient(url: string, token: string, sessionId: string, mjAPIKey: string, userAPIKey?: string): GraphQLClient {\n // Enhanced logging to diagnose token issues\n // const tokenPreview = token ? `${token.substring(0, 20)}...${token.substring(token.length - 10)}` : 'NO TOKEN';\n // console.log('[GraphQL] Creating new client:', {\n // url,\n // tokenPreview,\n // tokenLength: token?.length,\n // sessionId,\n // hasMJAPIKey: !!mjAPIKey,\n // hasUserAPIKey: !!userAPIKey\n // });\n\n const headers: Record<string, string> = {\n 'x-session-id': sessionId,\n };\n if (token)\n headers.authorization = 'Bearer ' + token;\n if (mjAPIKey)\n headers['x-mj-api-key'] = mjAPIKey;\n if (userAPIKey)\n headers['x-api-key'] = userAPIKey;\n\n return new GraphQLClient(url, {\n headers\n });\n }\n\n private _innerCurrentUserQueryString = `CurrentUser {\n ${this.userInfoString()}\n MJUserRoles_UserIDArray {\n ${this.userRoleInfoString()}\n }\n }\n `\n\n\n private _currentUserQuery = gql`query CurrentUserAndRoles {\n ${this._innerCurrentUserQueryString}\n }`\n\n\n\n private userInfoString(): string {\n return this.infoString(new UserInfo(null, null))\n }\n private userRoleInfoString(): string {\n return this.infoString(new UserRoleInfo(null))\n }\n private infoString(object: any): string {\n let sOutput: string = '';\n const keys = Object.keys(object)\n for (const k of keys) {\n if (k.startsWith('__mj_')) {\n sOutput += k.replace('__mj_', '_mj__') + '\\n '\n }\n else if (!k.startsWith('_')) {\n sOutput += k + '\\n '\n }\n }\n return sOutput\n }\n\n\n private _localStorageProvider: ILocalStorageProvider;\n get LocalStorageProvider(): ILocalStorageProvider {\n if (!this._localStorageProvider) {\n // Use BrowserIndexedDBStorageProvider in browser environments where indexedDB is available,\n // otherwise fall back to InMemoryLocalStorageProvider for Node.js/server environments\n if (typeof indexedDB !== 'undefined') {\n this._localStorageProvider = new BrowserIndexedDBStorageProvider();\n } else {\n this._localStorageProvider = new InMemoryLocalStorageProvider();\n }\n }\n\n return this._localStorageProvider;\n }\n\n\n /**************************************************************************/\n // END ---- IMetadataProvider\n /**************************************************************************/\n protected get Metadata(): IMetadataProvider {\n return this;\n }\n\n private _wsClient: Client = null;\n private _wsClientCreatedAt: number = null;\n private _pushStatusSubjects: Map<string, {\n subject: Subject<string>,\n subscription: Subscription,\n createdAt: number,\n lastRequestedAt: number,\n lastEmissionAt: number,\n activeSubscribers: number\n }> = new Map();\n // Tracks total WebSocket subscriptions (not component subscribers)\n // Used to prevent disposing client when subscriptions are active\n private _activeSubscriptionCount = 0;\n private readonly WS_CLIENT_MAX_AGE_MS = 30 * 60 * 1000; // 30 minutes\n private readonly SUBSCRIPTION_CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes\n private readonly SUBSCRIPTION_IDLE_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes\n private _subscriptionCleanupTimer: NodeJS.Timeout = null;\n private _isCleaningUp = false; // Prevent concurrent cleanup\n\n /**\n * Gets or creates a WebSocket client with health checking and automatic cleanup\n * @returns Active WebSocket client\n */\n private getOrCreateWSClient(): Client {\n const now = Date.now();\n\n // Check if existing client is too old and should be recreated\n if (this._wsClient && this._wsClientCreatedAt) {\n const age = now - this._wsClientCreatedAt;\n if (age > this.WS_CLIENT_MAX_AGE_MS && this._activeSubscriptionCount === 0) {\n // Client is old and no active subscriptions, recreate it\n this.disposeWSClient();\n }\n }\n\n // Create new client if needed\n if (!this._wsClient) {\n this._wsClient = createClient({\n url: this.ConfigData.WSURL,\n connectionParams: {\n Authorization: 'Bearer ' + this.ConfigData.Token,\n },\n keepAlive: 30000, // Send keepalive ping every 30 seconds\n retryAttempts: 3,\n shouldRetry: () => true,\n });\n this._wsClientCreatedAt = now;\n\n // Start cleanup timer if not already running\n if (!this._subscriptionCleanupTimer) {\n this._subscriptionCleanupTimer = setInterval(() => {\n this.cleanupStaleSubscriptions();\n }, this.SUBSCRIPTION_CLEANUP_INTERVAL_MS);\n }\n }\n\n return this._wsClient;\n }\n\n /**\n * Disposes of the WebSocket client\n * Does NOT complete subjects - caller should handle that separately to avoid double-cleanup\n */\n private disposeWSClient(): void {\n if (this._wsClient) {\n try {\n this._wsClient.dispose();\n } catch (e) {\n console.error('[GraphQLDataProvider] Error disposing WebSocket client:', e);\n }\n this._wsClient = null;\n this._wsClientCreatedAt = null;\n }\n }\n\n /**\n * Completes all subjects and clears the cache\n * Separate from disposeWSClient to avoid double-cleanup\n */\n private completeAllSubjects(): void {\n this._pushStatusSubjects.forEach((entry, sessionId) => {\n try {\n entry.subject.complete();\n entry.subscription.unsubscribe();\n } catch (e) {\n console.error(`[GraphQLDataProvider] Error cleaning up subject for ${sessionId}:`, e);\n }\n });\n this._pushStatusSubjects.clear();\n }\n\n /**\n * Cleans up stale subscription subjects that haven't been used recently\n * Uses cleanup flag to prevent concurrent execution\n * Uses Map snapshot to avoid concurrent modification issues\n */\n private cleanupStaleSubscriptions(): void {\n // Prevent concurrent cleanup\n if (this._isCleaningUp) {\n return;\n }\n this._isCleaningUp = true;\n\n try {\n const now = Date.now();\n const initialCount = this._pushStatusSubjects.size;\n\n // Create snapshot to avoid concurrent modification during iteration\n const entries = Array.from(this._pushStatusSubjects.entries());\n const toRemove: string[] = [];\n\n // Identify stale subscriptions (must have no active subscribers AND be idle)\n entries.forEach(([sessionId, value]) => {\n const timeSinceRequested = now - value.lastRequestedAt;\n const timeSinceEmission = now - value.lastEmissionAt;\n\n // Clean up if ALL conditions are true:\n // 1. No active subscribers (no component is listening)\n // 2. Not requested recently (no component has requested it)\n // 3. Not receiving data (no active server communication)\n const shouldCleanup = value.activeSubscribers === 0 &&\n timeSinceRequested >= this.SUBSCRIPTION_IDLE_TIMEOUT_MS &&\n timeSinceEmission >= this.SUBSCRIPTION_IDLE_TIMEOUT_MS;\n\n if (shouldCleanup) {\n console.log(`[GraphQLDataProvider] Marking session ${sessionId} for cleanup: ` +\n `activeSubscribers=${value.activeSubscribers}, ` +\n `timeSinceRequested=${Math.round(timeSinceRequested/1000)}s, ` +\n `timeSinceEmission=${Math.round(timeSinceEmission/1000)}s`);\n toRemove.push(sessionId);\n }\n });\n\n // Complete subjects and unsubscribe from WebSocket\n toRemove.forEach(sessionId => {\n const entry = this._pushStatusSubjects.get(sessionId);\n if (entry) {\n try {\n entry.subject.complete(); // Completes for ALL subscribers\n entry.subscription.unsubscribe(); // Closes WebSocket subscription\n this._pushStatusSubjects.delete(sessionId);\n console.log(`[GraphQLDataProvider] Cleaned up stale subscription for session: ${sessionId}`);\n } catch (e) {\n console.error(`[GraphQLDataProvider] Error cleaning up subscription for ${sessionId}:`, e);\n }\n }\n });\n\n if (toRemove.length > 0) {\n console.log(`[GraphQLDataProvider] Cleaned up ${toRemove.length} stale subscription(s)`);\n }\n\n // If no subscriptions remain and client is old, dispose of it\n if (this._pushStatusSubjects.size === 0 && this._wsClient && this._wsClientCreatedAt) {\n const clientAge = now - this._wsClientCreatedAt;\n if (clientAge > this.WS_CLIENT_MAX_AGE_MS) {\n console.log('[GraphQLDataProvider] Disposing of idle WebSocket client');\n this.disposeWSClient();\n }\n }\n } finally {\n this._isCleaningUp = false;\n }\n }\n\n /**\n * Generic subscription method for GraphQL subscriptions\n * @param subscription The GraphQL subscription query\n * @param variables Variables to pass to the subscription\n * @returns Observable that emits subscription data\n */\n public subscribe(subscription: string, variables?: any): Observable<any> {\n return new Observable((observer) => {\n const client = this.getOrCreateWSClient();\n this._activeSubscriptionCount++;\n\n const unsubscribe = client.subscribe(\n { query: subscription, variables },\n {\n next: (data) => {\n observer.next(data.data);\n },\n error: async (error: unknown) => {\n // Check if error is JWT_EXPIRED\n const errorObj = error as { extensions?: { code?: string }, message?: string };\n const isTokenExpired =\n errorObj?.extensions?.code === 'JWT_EXPIRED' ||\n errorObj?.message?.includes('token has expired') ||\n errorObj?.message?.includes('JWT_EXPIRED');\n\n if (isTokenExpired) {\n console.log('[GraphQLDataProvider] WebSocket JWT token expired, refreshing and reconnecting...');\n try {\n // Refresh the token\n await this.RefreshToken();\n\n // Dispose old WebSocket client\n this.disposeWSClient();\n\n // Observer will be completed, and caller should re-subscribe\n // which will create a new WebSocket connection with the new token\n observer.complete();\n } catch (refreshError) {\n console.error('[GraphQLDataProvider] Failed to refresh token for WebSocket:', refreshError);\n observer.error(refreshError);\n }\n } else {\n observer.error(error);\n }\n },\n complete: () => {\n observer.complete();\n },\n }\n );\n\n // Return cleanup function - this is ALWAYS called when subscription ends\n // whether by error, completion, or manual unsubscribe\n return () => {\n this._activeSubscriptionCount--;\n unsubscribe();\n };\n });\n }\n\n public PushStatusUpdates(sessionId: string = null): Observable<string> {\n if (!sessionId)\n sessionId = this.sessionId;\n\n const now = Date.now();\n\n // Check for existing subject\n const existing = this._pushStatusSubjects.get(sessionId);\n if (existing) {\n // Update last requested time\n existing.lastRequestedAt = now;\n // Wrap with defer to increment on subscribe and finalize to decrement on unsubscribe\n return new Observable<string>((observer) => {\n // Increment subscriber count when Observable is subscribed to\n existing.activeSubscribers++;\n\n // Subscribe to the underlying Subject\n const subscription = existing.subject.subscribe(observer);\n\n // Return teardown function that decrements count\n return () => {\n const entry = this._pushStatusSubjects.get(sessionId);\n if (entry && entry.activeSubscribers > 0) {\n entry.activeSubscribers--;\n }\n subscription.unsubscribe();\n };\n });\n }\n\n const SUBSCRIBE_TO_STATUS = gql`subscription StatusUpdates($sessionId: String!) {\n statusUpdates(sessionId: $sessionId) {\n date\n message\n sessionId\n }\n }\n `;\n\n // Create new Subject for status updates (no buffering - status updates are ephemeral)\n const subject = new Subject<string>();\n const client = this.getOrCreateWSClient();\n\n // Subscribe to WebSocket and pipe data to Subject\n const subscription = new Subscription();\n subscription.add(\n new Observable((observer) => {\n const unsubscribe = client.subscribe(\n { query: SUBSCRIBE_TO_STATUS, variables: { sessionId } },\n {\n next: (data: any) => {\n // Update last emission time\n const entry = this._pushStatusSubjects.get(sessionId);\n if (entry) {\n entry.lastEmissionAt = Date.now();\n }\n // Extract the message and emit to subject\n observer.next(data.data.statusUpdates.message);\n },\n error: async (error: unknown) => {\n // Check if error is JWT_EXPIRED\n const errorObj = error as { extensions?: { code?: string }, message?: string };\n const isTokenExpired =\n errorObj?.extensions?.code === 'JWT_EXPIRED' ||\n errorObj?.message?.includes('token has expired') ||\n errorObj?.message?.includes('JWT_EXPIRED');\n\n if (isTokenExpired) {\n console.log('[GraphQLDataProvider] PushStatusUpdates JWT token expired, refreshing and reconnecting...');\n try {\n // Refresh the token\n await this.RefreshToken();\n\n // Dispose old WebSocket client\n this.disposeWSClient();\n\n // Complete the subscription - components will auto-reconnect via RxJS retry logic\n observer.complete();\n } catch (refreshError) {\n console.error('[GraphQLDataProvider] Failed to refresh token for PushStatusUpdates:', refreshError);\n observer.error(refreshError);\n }\n } else {\n observer.error(error);\n }\n },\n complete: () => {\n observer.complete();\n },\n }\n );\n\n // Increment AFTER successful subscription setup\n this._activeSubscriptionCount++;\n\n return () => {\n this._activeSubscriptionCount--;\n unsubscribe();\n };\n }).subscribe({\n next: (message: string) => subject.next(message),\n error: (error) => {\n // On error, complete subject and remove from cache\n subject.error(error);\n this._pushStatusSubjects.delete(sessionId);\n },\n complete: () => {\n // On completion, complete subject and remove from cache\n subject.complete();\n this._pushStatusSubjects.delete(sessionId);\n }\n })\n );\n\n // Store subject with tracking data\n this._pushStatusSubjects.set(sessionId, {\n subject,\n subscription,\n createdAt: now,\n lastRequestedAt: now,\n lastEmissionAt: now,\n activeSubscribers: 0 // Will be incremented when first component subscribes\n });\n\n // Wrap return Observable to track subscribers\n return new Observable<string>((observer) => {\n // Increment subscriber count when Observable is subscribed to\n const entry = this._pushStatusSubjects.get(sessionId);\n if (entry) {\n entry.activeSubscribers++;\n }\n\n // Subscribe to the underlying Subject\n const sub = subject.subscribe(observer);\n\n // Return teardown function that decrements count\n return () => {\n const entry = this._pushStatusSubjects.get(sessionId);\n if (entry && entry.activeSubscribers > 0) {\n entry.activeSubscribers--;\n }\n sub.unsubscribe();\n };\n });\n }\n\n /**\n * Public method to dispose of WebSocket resources\n * Call this when shutting down the provider or on logout\n */\n public disposeWebSocketResources(): void {\n // Stop cleanup timer\n if (this._subscriptionCleanupTimer) {\n clearInterval(this._subscriptionCleanupTimer);\n this._subscriptionCleanupTimer = null;\n }\n\n // Complete all subjects and clear cache\n this.completeAllSubjects();\n\n // Reset counters\n this._activeSubscriptionCount = 0;\n\n // Dispose WebSocket client\n this.disposeWSClient();\n }\n\n /**************************************************************************\n * IS-A Child Entity Discovery\n *\n * Discovers which IS-A child entity, if any, has a record with the given\n * primary key. Calls the server-side FindISAChildEntity GraphQL query\n * which executes a single UNION ALL for efficiency.\n **************************************************************************/\n\n /**\n * Discovers which IS-A child entity has a record matching the given PK.\n * Calls the server-side FindISAChildEntity resolver via GraphQL.\n *\n * @param entityInfo The parent entity to check children for\n * @param recordPKValue The primary key value to search for in child tables\n * @param contextUser Optional context user (unused on client, present for interface parity)\n * @returns The child entity name if found, or null if no child record exists\n */\n public async FindISAChildEntity(\n entityInfo: EntityInfo,\n recordPKValue: string,\n contextUser?: UserInfo\n ): Promise<{ ChildEntityName: string } | null> {\n if (!entityInfo.IsParentType) return null;\n\n const gql = `query FindISAChildEntity($EntityName: String!, $RecordID: String!) {\n FindISAChildEntity(EntityName: $EntityName, RecordID: $RecordID) {\n Success\n ChildEntityName\n ErrorMessage\n }\n }`;\n\n try {\n const result = await this.ExecuteGQL(gql, {\n EntityName: entityInfo.Name,\n RecordID: recordPKValue\n });\n\n if (result?.FindISAChildEntity?.Success && result.FindISAChildEntity.ChildEntityName) {\n return { ChildEntityName: result.FindISAChildEntity.ChildEntityName };\n }\n return null;\n }\n catch (e) {\n LogError(`FindISAChildEntity failed for ${entityInfo.Name}: ${e}`);\n return null;\n }\n }\n\n /**\n * Discovers ALL IS-A child entities that have records matching the given PK.\n * Used for overlapping subtype parents (AllowMultipleSubtypes = true).\n * Calls the server-side FindISAChildEntities resolver via GraphQL.\n *\n * @param entityInfo The parent entity to check children for\n * @param recordPKValue The primary key value to search for in child tables\n * @param contextUser Optional context user (unused on client, present for interface parity)\n * @returns Array of child entity names found (empty if none)\n */\n public async FindISAChildEntities(\n entityInfo: EntityInfo,\n recordPKValue: string,\n contextUser?: UserInfo\n ): Promise<{ ChildEntityName: string }[]> {\n if (!entityInfo.IsParentType) return [];\n\n const gql = `query FindISAChildEntities($EntityName: String!, $RecordID: String!) {\n FindISAChildEntities(EntityName: $EntityName, RecordID: $RecordID) {\n Success\n ChildEntityNames\n ErrorMessage\n }\n }`;\n\n try {\n const result = await this.ExecuteGQL(gql, {\n EntityName: entityInfo.Name,\n RecordID: recordPKValue\n });\n\n if (result?.FindISAChildEntities?.Success && result.FindISAChildEntities.ChildEntityNames) {\n return result.FindISAChildEntities.ChildEntityNames.map(\n (name: string) => ({ ChildEntityName: name })\n );\n }\n return [];\n }\n catch (e) {\n LogError(`FindISAChildEntities failed for ${entityInfo.Name}: ${e}`);\n return [];\n }\n }\n}\n\n","import { RunReport, BaseEntity, Metadata, RunView, RunQuery, SetProvider, StartupManager } from \"@memberjunction/core\";\nimport { GraphQLDataProvider, GraphQLProviderConfigData } from \"./graphQLDataProvider\";\nimport { MJGlobal, MJEventType } from \"@memberjunction/global\";\n\n/**\n * Setup the GraphQL client for the project using the provided configuration data.\n */\nexport async function setupGraphQLClient(config: GraphQLProviderConfigData): Promise<GraphQLDataProvider> {\n // Set the provider for all entities to be GraphQL in this project, can use a different provider in other situations....\n const provider = new GraphQLDataProvider()\n\n // BaseEntity + Metadata share the same GraphQLDataProvider instance\n SetProvider(provider);\n\n await provider.Config(config);\n\n // fire off the logged in event if we get here\n await StartupManager.Instance.Startup();\n \n MJGlobal.Instance.RaiseEvent({ event: MJEventType.LoggedIn, eventCode: null, component: this, args: null });\n\n return provider;\n}","import { CompositeKey } from \"@memberjunction/core\";\n\n\n\nexport class SyncRolesAndUsersResult {\n Success: boolean;\n}\n \nexport class RoleInput {\n ID: string;\n\n Name: string;\n \n Description: string;\n}\n\n\nexport class UserInput {\n ID!: string;\n\n Name!: string;\n\n Email!: string;\n\n Type!: 'Owner' | 'User';\n\n FirstName: string;\n\n LastName: string;\n \n Title: string;\n\n Roles?: RoleInput[];\n}\n\nexport class RolesAndUsersInput {\n public Users: UserInput[];\n \n public Roles: RoleInput[];\n}\n\n\n\n/**\n * This type defines the possible list of actions that can be taken in syncing data: Create, Update, CreateOrUpdate, Delete, or DeleteWithFilter\n * DeleteWithFilter is where you specify a valid SQL expression that can be used in a where clause to get a list of records in a given entity to delete\n * this can be used to ensure cleaning out data from a subset of a given table.\n */\nexport enum SyncDataAction {\n Create = \"Create\",\n Update = \"Update\",\n CreateOrUpdate = \"CreateOrUpdate\",\n Delete = \"Delete\",\n DeleteWithFilter = \"DeleteWithFilter\"\n}\n \nexport class ActionItemInput {\n /**\n * The name of the entity where action is to be taken\n */\n EntityName!: string;\n /**\n * For Update, CreateOrUpdate and Delete operations either a PrimaryKey or an AlternateKey must be provided. For Create and DeleteWithFilter operations, neither is used. \n */\n PrimaryKey?: CompositeKey;\n /**\n * For Update, CreateOrUpdate and Delete operations either a PrimaryKey or an AlternateKey must be provided. For Create and DeleteWithFilter operations, neither is used. \n */\n AlternateKey?: CompositeKey;\n /**\n * The type of action requested. The possible values are Create, Update, CreateOrUpdate, Delete, DeleteWithFilter\n */\n Type!: SyncDataAction;\n /**\n * This field is a JSON representation of the field values of the entity to be created or updated. It is used for all ActionTypes except for \n */\n RecordJSON?: string;\n\n /**\n * This field is only provided when the Action Type is DeleteWithFilter. It is a valid SQL expression that can be used in a where clause to get a list of records in a given entity to delete\n */\n DeleteFilter?: string;\n}\n \n\nexport class SyncDataResult {\n Success: boolean;\n \n Results: ActionItemOutput[] = [];\n}\n\nexport class ActionItemOutput {\n Success: boolean;\n ErrorMessage: string;\n EntityName!: string;\n PrimaryKey?: CompositeKey;\n AlternateKey?: CompositeKey;\n Type!: SyncDataAction;\n\n /**\n * This field is a JSON representation of the field values of the entity to be created or updated. It is used for all ActionTypes except for \n */\n RecordJSON?: string;\n\n /**\n * This field is only provided when the Action Type is DeleteWithFilter. It is a valid SQL expression that can be used in a where clause to get a list of records in a given entity to delete\n */\n DeleteFilter?: string;\n}","import { CompositeKey, LogError, KeyValuePair, IsVerboseLoggingEnabled } from '@memberjunction/core';\nimport { SafeJSONParse } from '@memberjunction/global';\nimport { gql, GraphQLClient } from 'graphql-request'\nimport { ActionItemInput, RolesAndUsersInput, SyncDataResult, SyncRolesAndUsersResult } from './rolesAndUsersType';\nimport { \n RunAIPromptParams, \n RunAIPromptResult, \n ExecuteSimplePromptParams,\n SimplePromptResult,\n EmbedTextParams,\n EmbedTextResult\n} from './graphQLAIClient';\nimport { ExecuteAgentParams, ExecuteAgentResult } from '@memberjunction/ai-core-plus';\n\n/**\n * Specialized client that is designed to be used exclusively on the server side\n * by the System User using the MJ API KEY. This is designed to allow server side\n * code such as within the context of an MJAPI server to talk to another MAJPI server\n * but as the client.\n * \n * It is possible for a server to use the regular @GraphQLDataProvider client to talk\n * to another MJAPI server, but that would require the server to have a user account\n * and the server would have to be able to log in as that user via a JWT token. This\n * is not always possible or convenient in the flow. \n * \n * Also the standard client configuration process has a certain amount over overhead\n * in always loading up certain metadata that is not necessary for many system user\n * operations.\n * \n * Finaly, this client can be used in parallel with the regular client to allow a server\n * to mix and match the two as needed.\n */\nexport class GraphQLSystemUserClient {\n private _client: GraphQLClient;\n private _sessionId: string;\n /**\n * Returns the underlying GraphQL client which is an instance of the GraphQLClient object\n * from the graphql-request package. This is useful if you want to perform any operation\n * that is not directly supported by this class via specialized methods.\n */\n public get Client(): GraphQLClient {\n return this._client;\n }\n\n /**\n * @param url MJAPI server URL\n * @param token Optional, JWT token that is used for a normal user authentication flow. This is required if mjAPIKey is not provided.\n * @param sessionId Optional, UUID that is used to track a session. This can be any string.\n * @param mjAPIKey Shared Secret key that is provided for system user authentication. This is required if token is not provided.\n * @returns \n */\n constructor (url: string, token: string, sessionId: string, mjAPIKey: string) {\n const headers: Record<string, string> = { \n 'x-session-id': sessionId,\n };\n this._sessionId = sessionId;\n if (token)\n headers.authorization = 'Bearer ' + token;\n if (mjAPIKey)\n headers['x-mj-api-key'] = mjAPIKey;\n\n this._client = new GraphQLClient(url, {\n headers\n });\n }\n\n /**\n * Calls the GetData() query on the server to execute any number of provided SQL queries in parallel and returns the results.\n * The queries MUST BE read only and not perform any DML operations. The remote server will execute the queries using a special\n * lower-privilege user that is not allowed to perform any form of write operations.\n * @param queries an array of SQL queries to execute on the remote server\n * @param accessToken the short-lived access token that is required to perform this operation. This is different from the MJAPI key and is used for a second factor and is a short-lived token. You will receive this token \n * when an MJAPI calls your server to request something along with the URL to call back.\n * @returns \n */\n public async GetData(queries: string[], accessToken: string): Promise<GetDataOutput> {\n try {\n const query = `query GetData($input: GetDataInputType!) {\n GetData(input: $input) {\n Success\n ErrorMessages\n Queries\n Results\n }\n }`\n const result = await this.Client.request(query, {input: {Queries: queries, Token: accessToken}}) as {GetData: GetDataOutput};\n if (result && result.GetData) {\n // for each succesful item, we will parse and return the array of objects instead of the string\n return {\n Success: result.GetData.Success,\n Results: result.GetData.Results.map(r => r ? SafeJSONParse(r) : null),\n ErrorMessages: result.GetData.ErrorMessages,\n Queries: result.GetData.Queries \n }\n }\n else {\n return {\n Success: false,\n Results: [],\n ErrorMessages: result.GetData?.ErrorMessages ?? ['Unknown error'],\n Queries: result.GetData?.Queries ?? queries\n }\n }\n }\n catch (e) {\n // Extract clean error message - the GraphQL error response contains the actual SQL error\n let cleanError = e instanceof Error ? e.message : String(e);\n\n // Try to extract just the SQL error from GraphQL response\n // Look for the actual error message before the JSON payload\n const match = cleanError.match(/Error: ([^:]+)\\./);\n if (match) {\n cleanError = match[1] + '.';\n }\n\n // Only log verbose details if in verbose mode\n if (IsVerboseLoggingEnabled()) {\n const verboseError = `GraphQLSystemUserClient::GetData - Error getting data - ${e}`;\n LogError(verboseError);\n }\n\n return {\n Success: false,\n Results: [],\n ErrorMessages: [cleanError],\n Queries: queries\n }\n }\n }\n\n /**\n * This method will return a list of all entities that are available on the remote server. This is a lightweight call that only returns the basic metadata for each entity and does not include all of the attributes at \n * either the entity or the field level. This is useful for getting a list of entities that are available on the remote server and knowing their IDs and Entity Field IDs on the remote server. For core MemberJunction \n * entities and entity fields, the ID values are globally unique and set by the MemberJunction distribution, however, for other entities that are generated on each target system they can vary so it is best to use\n * lookups name or a combination of SchemaName and BaseTable to uniquely identify an entity.\n * @param client \n * @returns \n */\n public async GetAllRemoteEntities(): Promise<SimpleRemoteEntityOutput> {\n try {\n const query = `query GetAllEntities {\n GetAllEntities {\n Success\n ErrorMessage\n Results {\n ID\n Name\n Description\n SchemaName\n BaseView\n BaseTable\n CodeName\n ClassName\n Fields {\n ID\n Name\n Description\n Type\n AllowsNull\n MaxLength\n }\n }\n }\n }`\n\n const result = (await this.Client.request(query)) as {GetAllEntities: SimpleRemoteEntityOutput};\n if (result && result.GetAllEntities) {\n return result.GetAllEntities;\n }\n else {\n return {\n Success: false,\n Results: [],\n ErrorMessage: result.GetAllEntities?.ErrorMessage ?? 'Unknown error'\n }\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::GetAllRemoteEntities - Error getting remote entities - ${e}`);\n return {\n Success: false,\n Results: [],\n ErrorMessage: e\n }\n }\n }\n\n /**\n * This method is used to invoke the data synchronization mutation on the remote server. This method is used to create, update, or delete records in the remote server. The method takes an array of ActionItemInput objects\n * Each of the ActionItemInput objects represents a single action to take on a single entity. The action can be Create, Update, CreateOrUpdate, Delete, or DeleteWithFilter. The RecordJSON field is used for Create, CreateOrUpdate and Update whereas\n * the DeleteFilter field is used for DeleteWithFilter. The PrimaryKey and AlternateKey fields are used to identify the record to be acted upon. \n * \n * Use of the AlternateKey is important for situations where you might have divergent primary keys across systems. For example for user entities that are individually generated on each system by CodeGen, the primary key will\n * be different per system, so you would use the combination of the SchemaName and BaseTable to identify the entity and then use the AlternateKey to provide the combination of these fields to uniquely identify the record for updates.\n * @param items \n * @returns \n */\n public async SyncData(items: ActionItemInput[]): Promise<SyncDataResult> {\n try {\n // call the resolver to sync the roles and users\n const query = `mutation SyncData($items: [ActionItemInputType!]!) {\n SyncData(items: $items) {\n Success\n Results {\n Success\n ErrorMessage\n EntityName\n Type\n PrimaryKey {\n KeyValuePairs {\n FieldName\n Value \n }\n }\n AlternateKey {\n KeyValuePairs {\n FieldName\n Value \n }\n }\n DeleteFilter\n RecordJSON\n }\n }\n }`\n const d = <{SyncData: SyncDataResult}>await this.Client.request(query, {items});\n if (d && d.SyncData) {\n return d.SyncData;\n }\n else {\n return {\n Success: false,\n Results: []\n }\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::SyncData - Error syncing data - ${e}`);\n return {\n Success: false,\n Results: []\n }\n }\n }\n\n /**\n * This method will sync the roles and users on the remote server. This method is used to create, update, or delete roles and users on the remote server. \n * The method takes a RolesAndUsersInput object that contains an array of RoleInput objects. Note that this will not result in the removal of roles on the \n * remote server that are deemed to be built-in MemberJunction roles such as Developer, Integration and UI.\n * @param data \n * @returns \n */\n public async SyncRolesAndUsers(data: RolesAndUsersInput): Promise<SyncRolesAndUsersResult> {\n try {\n // call the resolver to sync the roles and users\n const query = `mutation SyncRolesAndUsers($data: RolesAndUsersInputType!) {\n SyncRolesAndUsers(data: $data) {\n Success\n }\n }`\n const d = await this.Client.request(query, {data}) as {SyncRolesAndUsers: SyncRolesAndUsersResult};\n if (d && d.SyncRolesAndUsers) {\n return d.SyncRolesAndUsers;\n }\n else {\n return {\n Success: false\n }\n }\n } \n catch (e) {\n LogError(`GraphQLSystemUserClient::SyncRolesAndUsers - Error syncing roles and users - ${e}`);\n return {\n Success: false\n }\n } \n }\n\n /**\n * Runs a view by name using the RunViewByNameSystemUser resolver.\n * @param input - View input parameters for running by name\n * @returns Promise containing the view execution results\n */\n public async RunViewByName(input: RunViewByNameSystemUserInput): Promise<RunViewSystemUserResult> {\n try {\n const query = `query RunViewByNameSystemUser($input: RunViewByNameInput!) {\n RunViewByNameSystemUser(input: $input) {\n Results {\n PrimaryKey {\n FieldName\n Value\n }\n EntityID\n Data\n }\n UserViewRunID\n RowCount\n TotalRowCount\n ExecutionTime\n ErrorMessage\n Success\n }\n }`\n\n const result = await this.Client.request(query, { input }) as { RunViewByNameSystemUser: RunViewSystemUserResult };\n if (result && result.RunViewByNameSystemUser) {\n return result.RunViewByNameSystemUser;\n } else {\n return {\n Results: [],\n Success: false,\n ErrorMessage: 'Failed to execute view by name'\n };\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::RunViewByNameSystemUser - Error running view by name - ${e}`);\n return {\n Results: [],\n Success: false,\n ErrorMessage: e.toString()\n };\n }\n }\n\n /**\n * Runs a view by ID using the RunViewByIDSystemUser resolver.\n * @param input - View input parameters for running by ID\n * @returns Promise containing the view execution results\n */\n public async RunViewByID(input: RunViewByIDSystemUserInput): Promise<RunViewSystemUserResult> {\n try {\n const query = `query RunViewByIDSystemUser($input: RunViewByIDInput!) {\n RunViewByIDSystemUser(input: $input) {\n Results {\n PrimaryKey {\n FieldName\n Value\n }\n EntityID\n Data\n }\n UserViewRunID\n RowCount\n TotalRowCount\n ExecutionTime\n ErrorMessage\n Success\n }\n }`\n\n const result = await this.Client.request(query, { input }) as { RunViewByIDSystemUser: RunViewSystemUserResult };\n if (result && result.RunViewByIDSystemUser) {\n return result.RunViewByIDSystemUser;\n } else {\n return {\n Results: [],\n Success: false,\n ErrorMessage: 'Failed to execute view by ID'\n };\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::RunViewByIDSystemUser - Error running view by ID - ${e}`);\n return {\n Results: [],\n Success: false,\n ErrorMessage: e.toString()\n };\n }\n }\n\n /**\n * Runs a dynamic view using the RunDynamicViewSystemUser resolver.\n * @param input - View input parameters for dynamic view execution\n * @returns Promise containing the view execution results\n */\n public async RunDynamicView(input: RunDynamicViewSystemUserInput): Promise<RunViewSystemUserResult> {\n try {\n const query = `query RunDynamicViewSystemUser($input: RunDynamicViewInput!) {\n RunDynamicViewSystemUser(input: $input) {\n Results {\n PrimaryKey {\n FieldName\n Value\n }\n EntityID\n Data\n }\n UserViewRunID\n RowCount\n TotalRowCount\n ExecutionTime\n ErrorMessage\n Success\n }\n }`\n\n const result = await this.Client.request(query, { input }) as { RunDynamicViewSystemUser: RunViewSystemUserResult };\n if (result && result.RunDynamicViewSystemUser) {\n return result.RunDynamicViewSystemUser;\n } else {\n return {\n Results: [],\n Success: false,\n ErrorMessage: 'Failed to execute dynamic view'\n };\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::RunDynamicViewSystemUser - Error running dynamic view - ${e}`);\n return {\n Results: [],\n Success: false,\n ErrorMessage: e.toString()\n };\n }\n }\n\n /**\n * Runs multiple views using the RunViewsSystemUser resolver. This method allows system users\n * to execute view queries with the same functionality as regular users but with system-level privileges.\n * @param input - Array of view input parameters\n * @returns Promise containing the results from all view executions\n */\n public async RunViews(input: RunViewSystemUserInput[]): Promise<RunViewSystemUserResult[]> {\n try {\n const query = `query RunViewsSystemUser($input: [RunViewGenericInput!]!) {\n RunViewsSystemUser(input: $input) {\n Results {\n PrimaryKey {\n FieldName\n Value\n }\n EntityID\n Data\n }\n UserViewRunID\n RowCount\n TotalRowCount\n ExecutionTime\n ErrorMessage\n Success\n }\n }`\n\n const result = await this.Client.request(query, { input }) as { RunViewsSystemUser: RunViewSystemUserResult[] };\n if (result && result.RunViewsSystemUser) {\n return result.RunViewsSystemUser;\n } else {\n return [];\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::RunViewsSystemUser - Error running views - ${e}`);\n return [];\n }\n }\n\n /**\n * Executes a stored query by ID using the GetQueryDataSystemUser resolver.\n * @param input - Query input parameters for execution by ID\n * @returns Promise containing the query execution results\n */\n public async GetQueryData(input: GetQueryDataSystemUserInput): Promise<RunQuerySystemUserResult> {\n try {\n // Validate that Parameters is a JSON object, not an array\n if (input.Parameters !== undefined && Array.isArray(input.Parameters)) {\n throw new Error('Parameters must be a JSON object, not an array. Use {} for empty parameters instead of [].');\n }\n\n const query = `query GetQueryDataSystemUser($QueryID: String!, $CategoryID: String, $CategoryPath: String, $Parameters: JSONObject, $MaxRows: Int, $StartRow: Int) {\n GetQueryDataSystemUser(QueryID: $QueryID, CategoryID: $CategoryID, CategoryPath: $CategoryPath, Parameters: $Parameters, MaxRows: $MaxRows, StartRow: $StartRow) {\n QueryID\n QueryName\n Success\n Results\n RowCount\n TotalRowCount\n ExecutionTime\n ErrorMessage\n AppliedParameters\n }\n }`\n\n const variables: any = { QueryID: input.QueryID };\n if (input.CategoryID !== undefined) variables.CategoryID = input.CategoryID;\n if (input.CategoryPath !== undefined) variables.CategoryPath = input.CategoryPath;\n if (input.Parameters !== undefined) variables.Parameters = input.Parameters;\n if (input.MaxRows !== undefined) variables.MaxRows = input.MaxRows;\n if (input.StartRow !== undefined) variables.StartRow = input.StartRow;\n\n const result = await this.Client.request(query, variables) as { GetQueryDataSystemUser: RunQuerySystemUserResult };\n \n if (result && result.GetQueryDataSystemUser) {\n // Parse the JSON results for easier consumption\n return {\n ...result.GetQueryDataSystemUser,\n Results: result.GetQueryDataSystemUser.Results ? SafeJSONParse(result.GetQueryDataSystemUser.Results) : null\n };\n } else {\n return {\n QueryID: input.QueryID,\n QueryName: '',\n Success: false,\n Results: null,\n RowCount: 0,\n TotalRowCount: 0,\n ExecutionTime: 0,\n ErrorMessage: 'Query execution failed'\n };\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::GetQueryDataSystemUser - Error executing query - ${e}`);\n return {\n QueryID: input.QueryID,\n QueryName: '',\n Success: false,\n Results: null,\n RowCount: 0,\n TotalRowCount: 0,\n ExecutionTime: 0,\n ErrorMessage: e.toString()\n };\n }\n }\n\n /**\n * Executes a stored query by name using the GetQueryDataByNameSystemUser resolver.\n * @param input - Query input parameters for execution by name\n * @returns Promise containing the query execution results\n */\n public async GetQueryDataByName(input: GetQueryDataByNameSystemUserInput): Promise<RunQuerySystemUserResult> {\n try {\n // Validate that Parameters is a JSON object, not an array\n if (input.Parameters !== undefined && Array.isArray(input.Parameters)) {\n throw new Error('Parameters must be a JSON object, not an array. Use {} for empty parameters instead of [].');\n }\n\n const query = `query GetQueryDataByNameSystemUser($QueryName: String!, $CategoryID: String, $CategoryPath: String, $Parameters: JSONObject, $MaxRows: Int, $StartRow: Int) {\n GetQueryDataByNameSystemUser(QueryName: $QueryName, CategoryID: $CategoryID, CategoryPath: $CategoryPath, Parameters: $Parameters, MaxRows: $MaxRows, StartRow: $StartRow) {\n QueryID\n QueryName\n Success\n Results\n RowCount\n TotalRowCount\n ExecutionTime\n ErrorMessage\n AppliedParameters\n }\n }`\n\n const variables: any = { QueryName: input.QueryName };\n if (input.CategoryID !== undefined) variables.CategoryID = input.CategoryID;\n if (input.CategoryPath !== undefined) variables.CategoryPath = input.CategoryPath;\n if (input.Parameters !== undefined) variables.Parameters = input.Parameters;\n if (input.MaxRows !== undefined) variables.MaxRows = input.MaxRows;\n if (input.StartRow !== undefined) variables.StartRow = input.StartRow;\n\n const result = await this.Client.request(query, variables) as { GetQueryDataByNameSystemUser: RunQuerySystemUserResult };\n \n if (result && result.GetQueryDataByNameSystemUser) {\n // Parse the JSON results for easier consumption\n return {\n ...result.GetQueryDataByNameSystemUser,\n Results: result.GetQueryDataByNameSystemUser.Results ? SafeJSONParse(result.GetQueryDataByNameSystemUser.Results) : null\n };\n } else {\n return {\n QueryID: '',\n QueryName: input.QueryName,\n Success: false,\n Results: null,\n RowCount: 0,\n TotalRowCount: 0,\n ExecutionTime: 0,\n ErrorMessage: 'Query execution failed'\n };\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::GetQueryDataByNameSystemUser - Error executing query - ${e}`);\n return {\n QueryID: '',\n QueryName: input.QueryName,\n Success: false,\n Results: null,\n RowCount: 0,\n TotalRowCount: 0,\n ExecutionTime: 0,\n ErrorMessage: e.toString()\n };\n }\n }\n\n /**\n * Creates a new query using the CreateQuerySystemUser mutation. This method is restricted to system users only.\n * @param input - CreateQuerySystemUserInput containing all the query attributes including optional CategoryPath\n * @returns Promise containing the result of the query creation\n */\n public async CreateQuery(input: CreateQueryInput): Promise<CreateQueryResult> {\n try {\n const query = `mutation CreateQuerySystemUser($input: CreateQuerySystemUserInput!) {\n CreateQuerySystemUser(input: $input) {\n Success\n ErrorMessage\n ID\n Name\n Description\n CategoryID\n Category\n SQL\n Status\n QualityRank\n EmbeddingVector\n EmbeddingModelID\n EmbeddingModelName\n Fields {\n ID\n QueryID\n Name\n Description\n Sequence\n SQLBaseType\n SQLFullType\n SourceEntityID\n SourceEntity\n SourceFieldName\n IsComputed\n ComputationDescription\n IsSummary\n SummaryDescription\n }\n Parameters {\n ID\n QueryID\n Name\n Description\n Type\n IsRequired\n DefaultValue\n SampleValue\n ValidationFilters\n }\n Entities {\n ID\n QueryID\n EntityID\n Entity\n }\n Permissions {\n ID\n QueryID\n RoleID\n Role\n }\n }\n }`\n\n const result = await this.Client.request(query, { input }) as { CreateQuerySystemUser: CreateQueryResult };\n if (result && result.CreateQuerySystemUser) {\n return result.CreateQuerySystemUser;\n } else {\n return {\n Success: false,\n ErrorMessage: 'Failed to create query'\n };\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::CreateQuery - Error creating query - ${e}`);\n return {\n Success: false,\n ErrorMessage: e.toString()\n };\n }\n }\n\n /**\n * Updates an existing query with the provided attributes. This method is restricted to system users only.\n * @param input - UpdateQueryInput containing the query ID and fields to update\n * @returns Promise containing the result of the query update including updated fields, parameters, entities, and permissions\n */\n public async UpdateQuery(input: UpdateQueryInput): Promise<UpdateQueryResult> {\n try {\n const query = `mutation UpdateQuerySystemUser($input: UpdateQuerySystemUserInput!) {\n UpdateQuerySystemUser(input: $input) {\n Success\n ErrorMessage\n ID\n Name\n Description\n CategoryID\n Category\n SQL\n Status\n QualityRank\n EmbeddingVector\n EmbeddingModelID\n EmbeddingModelName\n Fields {\n ID\n QueryID\n Name\n Description\n Sequence\n SQLBaseType\n SQLFullType\n SourceEntityID\n SourceEntity\n SourceFieldName\n IsComputed\n ComputationDescription\n IsSummary\n SummaryDescription\n }\n Parameters {\n ID\n QueryID\n Name\n Description\n Type\n IsRequired\n DefaultValue\n SampleValue\n ValidationFilters\n }\n Entities {\n ID\n QueryID\n EntityID\n Entity\n }\n Permissions {\n ID\n QueryID\n RoleID\n Role\n }\n }\n }`\n\n const result = await this.Client.request(query, { input }) as { UpdateQuerySystemUser: UpdateQueryResult };\n if (result && result.UpdateQuerySystemUser) {\n return result.UpdateQuerySystemUser;\n } else {\n return {\n Success: false,\n ErrorMessage: 'Failed to update query'\n };\n }\n }\n catch (e) {\n LogError(`GraphQLSystemUserClient::UpdateQuery - Error updating query - ${e}`);\n return {\n Success: false,\n ErrorMessage: e.toString()\n };\n }\n }\n\n /**\n * Deletes a query by ID using the DeleteQuerySystemResolver mutation. This method is restricted to system users only.\n * @param ID - The ID of the query to delete\n * @param options - Optional delete options controlling action execution\n * @returns Promise containing the result of the query deletion\n */\n public async DeleteQuery(ID: string, options?: DeleteQueryOptionsInput): Promise<DeleteQueryResult> {\n try {\n // Validate ID is not null/undefined/empty\n if (!ID || ID.trim() === '') {\n LogError('GraphQLSystemUserClient::DeleteQuery - Invalid query ID: ID cannot be null or empty');\n return {\n Success: false,\n ErrorMessage: 'Invalid query ID: ID cannot be null or empty'\n };\n }\n\n const query = `mutation DeleteQuerySystemResolver($ID: String!, $options: DeleteOptionsInput) {\n DeleteQuerySystemResolver(ID: $ID, options: $options) {\n Success\n ErrorMessage\n ID\n Name\n }\n }`\n\n const variables: Record<string, unknown> = { ID: ID };\n if (options !== undefined) {\n // Apply defaults for all required fields in DeleteOptionsInput\n // The server requires all fields to be present\n variables.options = {\n SkipEntityAIActions: options.SkipEntityAIActions ?? false,\n SkipEntityActions: options.SkipEntityActions ?? false,\n ReplayOnly: options.ReplayOnly ?? false,\n IsParentEntityDelete: options.IsParentEntityDelete ?? false\n };\n }\n\n const result = await this.Client.request(query, variables) as { DeleteQuerySystemResolver: DeleteQueryResult };\n \n if (result && result.DeleteQuerySystemResolver) {\n return result.DeleteQuerySystemResolver;\n } else {\n return {\n Success: false,\n ErrorMessage: 'Failed to delete query'\n };\n }\n }\n catch (e: unknown) {\n // Extract detailed error information for debugging\n let errorDetails = '';\n if (e instanceof Error) {\n errorDetails = e.message;\n // Check for cause (common in fetch errors)\n if ('cause' in e && e.cause) {\n const cause = e.cause as Error;\n errorDetails += ` | Cause: ${cause.message || cause}`;\n if ('code' in cause) {\n errorDetails += ` | Code: ${(cause as NodeJS.ErrnoException).code}`;\n }\n }\n // Check for response details (GraphQL client errors)\n if ('response' in e) {\n const response = (e as { response?: { status?: number; errors?: unknown[] } }).response;\n if (response?.status) {\n errorDetails += ` | HTTP Status: ${response.status}`;\n }\n if (response?.errors) {\n errorDetails += ` | GraphQL Errors: ${JSON.stringify(response.errors)}`;\n }\n }\n // Include stack trace for debugging\n if (e.stack) {\n console.error('DeleteQuery stack trace:', e.stack);\n }\n } else {\n errorDetails = String(e);\n }\n\n LogError(`GraphQLSystemUserClient::DeleteQuery - Error deleting query - ${errorDetails}`);\n return {\n Success: false,\n ErrorMessage: errorDetails\n };\n }\n }\n\n /**\n * Run an AI prompt with system user privileges.\n * This method allows system-level execution of AI prompts without user authentication.\n * \n * @param params The parameters for running the AI prompt\n * @returns A Promise that resolves to a RunAIPromptResult object\n * \n * @example\n * ```typescript\n * const result = await systemClient.RunAIPrompt({\n * promptId: \"prompt-id\",\n * data: { systemData: \"value\" },\n * skipValidation: true\n * });\n * ```\n */\n public async RunAIPrompt(params: RunAIPromptParams): Promise<RunAIPromptResult> {\n try {\n // Build the query for system user\n const query = gql`\n query RunAIPromptSystemUser(\n $promptId: String!,\n $data: String,\n $overrideModelId: String,\n $overrideVendorId: String,\n $configurationId: String,\n $skipValidation: Boolean,\n $templateData: String,\n $responseFormat: String,\n $temperature: Float,\n $topP: Float,\n $topK: Int,\n $minP: Float,\n $frequencyPenalty: Float,\n $presencePenalty: Float,\n $seed: Int,\n $stopSequences: [String!],\n $includeLogProbs: Boolean,\n $topLogProbs: Int,\n $messages: String,\n $rerunFromPromptRunID: String,\n $systemPromptOverride: String\n ) {\n RunAIPromptSystemUser(\n promptId: $promptId,\n data: $data,\n overrideModelId: $overrideModelId,\n overrideVendorId: $overrideVendorId,\n configurationId: $configurationId,\n skipValidation: $skipValidation,\n templateData: $templateData,\n responseFormat: $responseFormat,\n temperature: $temperature,\n topP: $topP,\n topK: $topK,\n minP: $minP,\n frequencyPenalty: $frequencyPenalty,\n presencePenalty: $presencePenalty,\n seed: $seed,\n stopSequences: $stopSequences,\n includeLogProbs: $includeLogProbs,\n topLogProbs: $topLogProbs,\n messages: $messages,\n rerunFromPromptRunID: $rerunFromPromptRunID,\n systemPromptOverride: $systemPromptOverride\n ) {\n success\n output\n parsedResult\n error\n executionTimeMs\n tokensUsed\n promptRunId\n rawResult\n validationResult\n chatResult\n }\n }\n `;\n\n // Prepare variables\n const variables = this.preparePromptVariables(params);\n\n // Execute the query\n const result = await this.Client.request(query, variables) as { RunAIPromptSystemUser: any };\n\n // Process and return the result\n if (result && result.RunAIPromptSystemUser) {\n return this.processPromptResult(result.RunAIPromptSystemUser);\n } else {\n return {\n success: false,\n error: 'Failed to execute AI prompt as system user'\n };\n }\n } catch (e) {\n LogError(`GraphQLSystemUserClient::RunAIPrompt - Error running AI prompt - ${e}`);\n return {\n success: false,\n error: e.toString()\n };\n }\n }\n\n /**\n * Run an AI agent with system user privileges.\n * This method allows system-level execution of AI agents without user authentication.\n * \n * @param params The parameters for running the AI agent\n * @returns A Promise that resolves to a RunAIAgentResult object\n * \n * @example\n * ```typescript\n * const result = await systemClient.RunAIAgent({\n * agentId: \"agent-id\",\n * messages: [{ role: \"system\", content: \"Process data\" }],\n * sessionId: \"system-session\"\n * });\n * ```\n */\n public async RunAIAgent(params: ExecuteAgentParams): Promise<ExecuteAgentResult> {\n try {\n // Build the query for system user\n const query = gql`\n query RunAIAgentSystemUser(\n $agentId: String!,\n $messages: String!,\n $sessionId: String!,\n $data: String,\n $templateData: String,\n $lastRunId: String,\n $autoPopulateLastRunPayload: Boolean,\n $configurationId: String\n ) {\n RunAIAgentSystemUser(\n agentId: $agentId,\n messages: $messages,\n sessionId: $sessionId,\n data: $data,\n templateData: $templateData,\n lastRunId: $lastRunId,\n autoPopulateLastRunPayload: $autoPopulateLastRunPayload,\n configurationId: $configurationId\n ) {\n success\n errorMessage\n executionTimeMs\n result\n }\n }\n `;\n\n // Prepare variables\n const variables = this.prepareAgentVariables(params);\n\n // Execute the query\n const result = await this.Client.request(query, variables) as { RunAIAgentSystemUser: any };\n\n // Process and return the result\n if (result && result.RunAIAgentSystemUser) {\n return this.processAgentResult(result.RunAIAgentSystemUser.result);\n } else {\n return {\n success: false, \n agentRun: undefined\n };\n }\n } catch (e) {\n LogError(`GraphQLSystemUserClient::RunAIAgent - Error running AI agent - ${e}`);\n return {\n success: false, \n agentRun: undefined\n };\n }\n }\n\n /**\n * Helper method to prepare prompt variables for GraphQL\n * @private\n */\n private preparePromptVariables(params: RunAIPromptParams): Record<string, any> {\n const variables: Record<string, any> = {\n promptId: params.promptId\n };\n\n // Serialize complex objects to JSON strings\n if (params.data !== undefined) {\n variables.data = typeof params.data === 'object' ? JSON.stringify(params.data) : params.data;\n }\n if (params.templateData !== undefined) {\n variables.templateData = typeof params.templateData === 'object' ? JSON.stringify(params.templateData) : params.templateData;\n }\n if (params.messages !== undefined) {\n variables.messages = JSON.stringify(params.messages);\n }\n\n // Add optional scalar parameters\n if (params.overrideModelId !== undefined) variables.overrideModelId = params.overrideModelId;\n if (params.overrideVendorId !== undefined) variables.overrideVendorId = params.overrideVendorId;\n if (params.configurationId !== undefined) variables.configurationId = params.configurationId;\n if (params.skipValidation !== undefined) variables.skipValidation = params.skipValidation;\n if (params.responseFormat !== undefined) variables.responseFormat = params.responseFormat;\n if (params.temperature !== undefined) variables.temperature = params.temperature;\n if (params.topP !== undefined) variables.topP = params.topP;\n if (params.topK !== undefined) variables.topK = params.topK;\n if (params.minP !== undefined) variables.minP = params.minP;\n if (params.frequencyPenalty !== undefined) variables.frequencyPenalty = params.frequencyPenalty;\n if (params.presencePenalty !== undefined) variables.presencePenalty = params.presencePenalty;\n if (params.seed !== undefined) variables.seed = params.seed;\n if (params.stopSequences !== undefined) variables.stopSequences = params.stopSequences;\n if (params.includeLogProbs !== undefined) variables.includeLogProbs = params.includeLogProbs;\n if (params.topLogProbs !== undefined) variables.topLogProbs = params.topLogProbs;\n if (params.rerunFromPromptRunID !== undefined) variables.rerunFromPromptRunID = params.rerunFromPromptRunID;\n if (params.systemPromptOverride !== undefined) variables.systemPromptOverride = params.systemPromptOverride;\n\n return variables;\n }\n\n /**\n * Helper method to prepare agent variables for GraphQL\n * @private\n */\n private prepareAgentVariables(params: ExecuteAgentParams): Record<string, any> {\n const variables: Record<string, any> = {\n agentId: params.agent.ID,\n messages: JSON.stringify(params.conversationMessages),\n sessionId: this._sessionId\n };\n\n // Serialize optional complex objects to JSON strings\n if (params.data !== undefined) {\n variables.data = typeof params.data === 'object' ? JSON.stringify(params.data) : params.data;\n }\n\n // Add optional scalar parameters\n if (params.lastRunId !== undefined) variables.lastRunId = params.lastRunId;\n if (params.autoPopulateLastRunPayload !== undefined) variables.autoPopulateLastRunPayload = params.autoPopulateLastRunPayload;\n if (params.configurationId !== undefined) variables.configurationId = params.configurationId;\n\n return variables;\n }\n\n /**\n * Helper method to process prompt results\n * @private\n */\n private processPromptResult(promptResult: any): RunAIPromptResult {\n // Parse JSON results if they exist\n let parsedResult: any;\n let validationResult: any;\n let chatResult: any;\n\n try {\n if (promptResult.parsedResult) {\n parsedResult = JSON.parse(promptResult.parsedResult);\n }\n } catch (e) {\n parsedResult = promptResult.parsedResult;\n }\n\n try {\n if (promptResult.validationResult) {\n validationResult = JSON.parse(promptResult.validationResult);\n }\n } catch (e) {\n validationResult = promptResult.validationResult;\n }\n\n try {\n if (promptResult.chatResult) {\n chatResult = JSON.parse(promptResult.chatResult);\n }\n } catch (e) {\n chatResult = promptResult.chatResult;\n }\n\n return {\n success: promptResult.success,\n output: promptResult.output,\n parsedResult,\n error: promptResult.error,\n executionTimeMs: promptResult.executionTimeMs,\n tokensUsed: promptResult.tokensUsed,\n promptRunId: promptResult.promptRunId,\n rawResult: promptResult.rawResult,\n validationResult,\n chatResult\n };\n }\n\n /**\n * Helper method to process agent results\n * @private\n */\n private processAgentResult(agentResult: any): ExecuteAgentResult {\n return SafeJSONParse(agentResult) as ExecuteAgentResult;\n }\n\n /**\n * Execute a simple prompt without requiring a stored AI Prompt entity.\n * This method allows system-level execution of simple prompts.\n * \n * @param params The parameters for the simple prompt execution\n * @returns A Promise that resolves to a SimplePromptResult object\n * \n * @example\n * ```typescript\n * const result = await systemClient.ExecuteSimplePrompt({\n * systemPrompt: \"You are a data analyst\",\n * modelPower: \"medium\"\n * });\n * ```\n */\n public async ExecuteSimplePrompt(params: ExecuteSimplePromptParams): Promise<SimplePromptResult> {\n try {\n const query = gql`\n query ExecuteSimplePromptSystemUser(\n $systemPrompt: String!,\n $messages: String,\n $preferredModels: [String!],\n $modelPower: String,\n $responseFormat: String\n ) {\n ExecuteSimplePromptSystemUser(\n systemPrompt: $systemPrompt,\n messages: $messages,\n preferredModels: $preferredModels,\n modelPower: $modelPower,\n responseFormat: $responseFormat\n ) {\n success\n result\n resultObject\n modelName\n error\n executionTimeMs\n }\n }\n `;\n\n // Prepare variables\n const variables: Record<string, any> = {\n systemPrompt: params.systemPrompt\n };\n\n // Convert messages array to JSON string if provided\n if (params.messages && params.messages.length > 0) {\n variables.messages = JSON.stringify(params.messages);\n }\n\n if (params.preferredModels) {\n variables.preferredModels = params.preferredModels;\n }\n\n if (params.modelPower) {\n variables.modelPower = params.modelPower;\n }\n\n if (params.responseFormat) {\n variables.responseFormat = params.responseFormat;\n }\n\n // Execute the query\n const result = await this.Client.request(query, variables) as { ExecuteSimplePromptSystemUser: any };\n\n if (!result?.ExecuteSimplePromptSystemUser) {\n return {\n success: false,\n modelName: 'Unknown',\n error: 'Failed to execute simple prompt as system user'\n };\n }\n\n const promptResult = result.ExecuteSimplePromptSystemUser;\n\n // Parse resultObject if it exists\n let resultObject: any;\n if (promptResult.resultObject) {\n try {\n resultObject = JSON.parse(promptResult.resultObject);\n } catch (e) {\n resultObject = promptResult.resultObject;\n }\n }\n\n return {\n success: promptResult.success,\n result: promptResult.result,\n resultObject,\n modelName: promptResult.modelName,\n error: promptResult.error,\n executionTimeMs: promptResult.executionTimeMs\n };\n\n } catch (e) {\n LogError(`GraphQLSystemUserClient::ExecuteSimplePrompt - Error executing simple prompt - ${e}`);\n return {\n success: false,\n modelName: 'Unknown',\n error: e.toString()\n };\n }\n }\n\n /**\n * Generate embeddings using local embedding models.\n * This method allows system-level generation of text embeddings.\n * \n * @param params The parameters for embedding generation\n * @returns A Promise that resolves to an EmbedTextResult object\n * \n * @example\n * ```typescript\n * const result = await systemClient.EmbedText({\n * textToEmbed: [\"System data\", \"Configuration\"],\n * modelSize: \"small\"\n * });\n * ```\n */\n public async EmbedText(params: EmbedTextParams): Promise<EmbedTextResult> {\n try {\n const query = gql`\n query EmbedTextSystemUser(\n $textToEmbed: [String!]!,\n $modelSize: String!\n ) {\n EmbedTextSystemUser(\n textToEmbed: $textToEmbed,\n modelSize: $modelSize\n ) {\n embeddings\n modelName\n vectorDimensions\n error\n }\n }\n `;\n\n // Prepare variables - handle both single string and array\n const textArray = Array.isArray(params.textToEmbed) \n ? params.textToEmbed \n : [params.textToEmbed];\n\n const variables = {\n textToEmbed: textArray,\n modelSize: params.modelSize\n };\n\n // Execute the query\n const result = await this.Client.request(query, variables) as { EmbedTextSystemUser: any };\n\n if (!result?.EmbedTextSystemUser) {\n return {\n embeddings: Array.isArray(params.textToEmbed) ? [] : [],\n modelName: 'Unknown',\n vectorDimensions: 0,\n error: 'Failed to generate embeddings as system user'\n };\n }\n\n const embedResult = result.EmbedTextSystemUser;\n\n // Return single embedding or array based on input\n const returnEmbeddings = Array.isArray(params.textToEmbed)\n ? embedResult.embeddings\n : embedResult.embeddings[0];\n\n return {\n embeddings: returnEmbeddings,\n modelName: embedResult.modelName,\n vectorDimensions: embedResult.vectorDimensions,\n error: embedResult.error\n };\n\n } catch (e) {\n LogError(`GraphQLSystemUserClient::EmbedText - Error generating embeddings - ${e}`);\n return {\n embeddings: Array.isArray(params.textToEmbed) ? [] : [],\n modelName: 'Unknown',\n vectorDimensions: 0,\n error: e.toString()\n };\n }\n }\n\n}\n\n/**\n * Output type for GetData calls - contains results from executing multiple SQL queries\n */\nexport class GetDataOutput {\n /**\n * Indicates if the operation was successful overall. If any individual query failed, this will be false. However, any successful queries will still be returned in the Results array.\n */\n Success: boolean;\n /**\n * The original input of Queries that were run - same order as provided in the request\n */\n Queries: string[];\n /**\n * An ordered array of error messages for each query that was run. This array will always have the same # of entries as Queries. If a query was successful, the corresponding entry will be null.\n */\n ErrorMessages: (string | null)[];\n /**\n * An ordered array of results for each query that was run. This array will always have the same # of entries as Queries. If a query failed, the corresponding entry will be null. Successful results are JSON strings containing the query data.\n */\n Results: (string | null)[];\n}\n\n/**\n * Return type for calls to the GetAllRemoteEntities query - provides lightweight entity metadata\n */\nexport class SimpleRemoteEntityOutput {\n /**\n * Indicates whether the remote entity retrieval was successful\n */\n Success: boolean;\n /**\n * Error message if the operation failed, undefined if successful\n */\n ErrorMessage?: string;\n /**\n * An array of simple entity types that are returned from the remote server - contains basic metadata for each entity\n */\n Results: SimpleRemoteEntity[];\n}\n\n/**\n * Represents a simple entity type that is used for lightweight retrieval of partial remote entity metadata \n */\nexport class SimpleRemoteEntity {\n /**\n * Unique identifier of the entity on the remote server\n */\n ID: string;\n /**\n * Display name of the entity (e.g., \"Users\", \"Companies\")\n */\n Name: string;\n /**\n * Optional description explaining the entity's purpose\n */\n Description?: string;\n /**\n * Database schema name where the entity resides (e.g., \"dbo\", \"custom\")\n */\n SchemaName: string;\n /**\n * Name of the database view used for reading this entity\n */\n BaseView: string;\n /**\n * Name of the database table used for storing this entity\n */\n BaseTable: string;\n /**\n * Optional code-friendly name for the entity (typically PascalCase)\n */\n CodeName?: string;\n /**\n * Optional TypeScript/JavaScript class name for the entity\n */\n ClassName?: string;\n /**\n * Array of field definitions for this entity\n */\n Fields: SimpleRemoteEntityField[];\n}\n\n/**\n * Represents a field within a remote entity - contains basic field metadata\n */\nexport class SimpleRemoteEntityField {\n /**\n * Unique identifier of the entity field on the remote server\n */\n ID: string;\n /**\n * Field name (e.g., \"FirstName\", \"Email\", \"CreatedAt\")\n */\n Name: string;\n /**\n * Optional description explaining the field's purpose\n */\n Description?: string;\n /**\n * Data type of the field (e.g., \"nvarchar\", \"int\", \"datetime\", \"bit\")\n */\n Type: string;\n /**\n * Whether the field can contain null values\n */\n AllowsNull: boolean;\n /**\n * Maximum length for string fields, -1 for unlimited, 0 for non-string types\n */\n MaxLength: number;\n}\n\n/**\n * Input type for RunViewByNameSystemUser method calls - executes a saved view by name\n */\nexport interface RunViewByNameSystemUserInput {\n /**\n * Name of the saved view to execute\n */\n ViewName: string;\n /**\n * Additional WHERE clause conditions to apply (optional)\n */\n ExtraFilter?: string;\n /**\n * ORDER BY clause for sorting results (optional)\n */\n OrderBy?: string;\n /**\n * Specific fields to return, if not specified returns all fields (optional)\n */\n Fields?: string[];\n /**\n * Search string to filter results across searchable fields (optional)\n */\n UserSearchString?: string;\n /**\n * ID of a previous view run to exclude results from (optional)\n */\n ExcludeUserViewRunID?: string;\n /**\n * Override the exclude filter with custom logic (optional)\n */\n OverrideExcludeFilter?: string;\n /**\n * Whether to save the view execution results for future reference (optional)\n */\n SaveViewResults?: boolean;\n /**\n * Whether to exclude data from all prior view runs (optional)\n */\n ExcludeDataFromAllPriorViewRuns?: boolean;\n /**\n * Whether to ignore the view's MaxRows setting and return all results (optional)\n */\n IgnoreMaxRows?: boolean;\n /**\n * Maximum number of rows to return, overrides view setting if specified (optional)\n */\n MaxRows?: number;\n /**\n * Whether to force audit logging for this view execution (optional)\n */\n ForceAuditLog?: boolean;\n /**\n * Description for the audit log entry if ForceAuditLog is true (optional)\n */\n AuditLogDescription?: string;\n /**\n * Type of result format: \"simple\", \"entity_object\", etc. (optional)\n */\n ResultType?: string;\n /**\n * Starting row number for pagination (optional, 0-based)\n */\n StartRow?: number;\n}\n\n/**\n * Input type for RunViewByIDSystemUser method calls - executes a saved view by its unique ID\n */\nexport interface RunViewByIDSystemUserInput {\n /**\n * Unique identifier of the saved view to execute\n */\n ViewID: string;\n /**\n * Additional WHERE clause conditions to apply (optional)\n */\n ExtraFilter?: string;\n /**\n * ORDER BY clause for sorting results (optional)\n */\n OrderBy?: string;\n /**\n * Specific fields to return, if not specified returns all fields (optional)\n */\n Fields?: string[];\n /**\n * Search string to filter results across searchable fields (optional)\n */\n UserSearchString?: string;\n /**\n * ID of a previous view run to exclude results from (optional)\n */\n ExcludeUserViewRunID?: string;\n /**\n * Override the exclude filter with custom logic (optional)\n */\n OverrideExcludeFilter?: string;\n /**\n * Whether to save the view execution results for future reference (optional)\n */\n SaveViewResults?: boolean;\n /**\n * Whether to exclude data from all prior view runs (optional)\n */\n ExcludeDataFromAllPriorViewRuns?: boolean;\n /**\n * Whether to ignore the view's MaxRows setting and return all results (optional)\n */\n IgnoreMaxRows?: boolean;\n /**\n * Maximum number of rows to return, overrides view setting if specified (optional)\n */\n MaxRows?: number;\n /**\n * Whether to force audit logging for this view execution (optional)\n */\n ForceAuditLog?: boolean;\n /**\n * Description for the audit log entry if ForceAuditLog is true (optional)\n */\n AuditLogDescription?: string;\n /**\n * Type of result format: \"simple\", \"entity_object\", etc. (optional)\n */\n ResultType?: string;\n /**\n * Starting row number for pagination (optional, 0-based)\n */\n StartRow?: number;\n}\n\n/**\n * Input type for RunDynamicViewSystemUser method calls - creates and executes a view dynamically based on entity\n */\nexport interface RunDynamicViewSystemUserInput {\n /**\n * Name of the entity to query (e.g., \"Users\", \"Companies\")\n */\n EntityName: string;\n /**\n * Additional WHERE clause conditions to apply (optional)\n */\n ExtraFilter?: string;\n /**\n * ORDER BY clause for sorting results (optional)\n */\n OrderBy?: string;\n /**\n * Specific fields to return, if not specified returns all fields (optional)\n */\n Fields?: string[];\n /**\n * Search string to filter results across searchable fields (optional)\n */\n UserSearchString?: string;\n /**\n * ID of a previous view run to exclude results from (optional)\n */\n ExcludeUserViewRunID?: string;\n /**\n * Override the exclude filter with custom logic (optional)\n */\n OverrideExcludeFilter?: string;\n /**\n * Whether to ignore MaxRows limits and return all results (optional)\n */\n IgnoreMaxRows?: boolean;\n /**\n * Maximum number of rows to return (optional)\n */\n MaxRows?: number;\n /**\n * Whether to force audit logging for this view execution (optional)\n */\n ForceAuditLog?: boolean;\n /**\n * Description for the audit log entry if ForceAuditLog is true (optional)\n */\n AuditLogDescription?: string;\n /**\n * Type of result format: \"simple\", \"entity_object\", etc. (optional)\n */\n ResultType?: string;\n /**\n * Starting row number for pagination (optional, 0-based)\n */\n StartRow?: number;\n}\n\n/**\n * Input type for RunViewsSystemUser method calls - executes multiple views in parallel\n */\nexport interface RunViewSystemUserInput {\n /**\n * Name of the entity to query (e.g., \"Users\", \"Companies\")\n */\n EntityName: string;\n /**\n * Additional WHERE clause conditions to apply (optional)\n */\n ExtraFilter?: string;\n /**\n * ORDER BY clause for sorting results (optional)\n */\n OrderBy?: string;\n /**\n * Specific fields to return, if not specified returns all fields (optional)\n */\n Fields?: string[];\n /**\n * Search string to filter results across searchable fields (optional)\n */\n UserSearchString?: string;\n /**\n * ID of a previous view run to exclude results from (optional)\n */\n ExcludeUserViewRunID?: string;\n /**\n * Override the exclude filter with custom logic (optional)\n */\n OverrideExcludeFilter?: string;\n /**\n * Whether to ignore MaxRows limits and return all results (optional)\n */\n IgnoreMaxRows?: boolean;\n /**\n * Maximum number of rows to return (optional)\n */\n MaxRows?: number;\n /**\n * Whether to force audit logging for this view execution (optional)\n */\n ForceAuditLog?: boolean;\n /**\n * Description for the audit log entry if ForceAuditLog is true (optional)\n */\n AuditLogDescription?: string;\n /**\n * Type of result format: \"simple\", \"entity_object\", etc. (optional)\n */\n ResultType?: string;\n /**\n * Starting row number for pagination (optional, 0-based)\n */\n StartRow?: number;\n}\n\n\n/**\n * Result row type for view execution results - represents a single data row\n */\nexport interface RunViewSystemUserResultRow {\n /**\n * Primary key fields and values for the record\n */\n PrimaryKey: KeyValuePair[];\n /**\n * ID of the entity type this record belongs to\n */\n EntityID: string;\n /**\n * JSON string containing the actual record data\n */\n Data: string;\n}\n\n/**\n * Result type for RunViewsSystemUser method calls - contains execution results and metadata\n */\nexport interface RunViewSystemUserResult {\n /**\n * Array of result rows containing the actual data\n */\n Results: RunViewSystemUserResultRow[];\n /**\n * Unique identifier for this view execution run (optional)\n */\n UserViewRunID?: string;\n /**\n * Number of rows returned in this result set (optional)\n */\n RowCount?: number;\n /**\n * Total number of rows available (before pagination) (optional)\n */\n TotalRowCount?: number;\n /**\n * Time taken to execute the view in milliseconds (optional)\n */\n ExecutionTime?: number;\n /**\n * Error message if the execution failed (optional)\n */\n ErrorMessage?: string;\n /**\n * Whether the view execution was successful\n */\n Success: boolean;\n}\n\n/**\n * Result type for query execution methods - contains query results and execution metadata\n */\nexport interface RunQuerySystemUserResult {\n /**\n * Unique identifier of the executed query\n */\n QueryID: string;\n /**\n * Display name of the executed query\n */\n QueryName: string;\n /**\n * Whether the query execution was successful\n */\n Success: boolean;\n /**\n * Query results data (parsed from JSON)\n */\n Results: any;\n /**\n * Number of rows returned by the query\n */\n RowCount: number;\n /**\n * Total number of rows available (before pagination)\n */\n TotalRowCount: number;\n /**\n * Time taken to execute the query in milliseconds\n */\n ExecutionTime: number;\n /**\n * Error message if the query execution failed\n */\n ErrorMessage: string;\n /**\n * JSON string containing the applied parameters (optional)\n */\n AppliedParameters?: string;\n}\n\n/**\n * Input type for GetQueryDataSystemUser method calls - executes a stored query by ID\n */\nexport interface GetQueryDataSystemUserInput {\n /**\n * The ID of the query to execute\n */\n QueryID: string;\n /**\n * Optional category ID filter\n */\n CategoryID?: string;\n /**\n * Optional category path filter (hierarchical path like \"/MJ/AI/Agents/\" or simple name)\n */\n CategoryPath?: string;\n /**\n * Optional parameters for templated queries\n */\n Parameters?: Record<string, any>;\n /**\n * Optional maximum number of rows to return\n */\n MaxRows?: number;\n /**\n * Optional starting row number for pagination\n */\n StartRow?: number;\n}\n\n/**\n * Input type for GetQueryDataByNameSystemUser method calls - executes a stored query by name\n */\nexport interface GetQueryDataByNameSystemUserInput {\n /**\n * The name of the query to execute\n */\n QueryName: string;\n /**\n * Optional category ID filter\n */\n CategoryID?: string;\n /**\n * Optional category path filter (hierarchical path like \"/MJ/AI/Agents/\" or simple name)\n */\n CategoryPath?: string;\n /**\n * Optional parameters for templated queries\n */\n Parameters?: Record<string, any>;\n /**\n * Optional maximum number of rows to return\n */\n MaxRows?: number;\n /**\n * Optional starting row number for pagination\n */\n StartRow?: number;\n}\n\n/**\n * Input type for query permissions to be created with a new query\n */\nexport interface QueryPermissionInput {\n /**\n * Role ID to grant access to\n */\n RoleID: string;\n}\n\n/**\n * Input type for CreateQuery mutation calls - creates a new query with optional hierarchical category path\n */\nexport interface CreateQueryInput {\n /**\n * Required name for the query (must be unique within category)\n */\n Name: string;\n /**\n * Optional existing category ID to assign the query to\n */\n CategoryID?: string;\n /**\n * Optional category path for automatic hierarchy creation (e.g., \"Reports/Sales/Monthly\") - takes precedence over CategoryID\n */\n CategoryPath?: string;\n /**\n * Optional natural language question this query answers\n */\n UserQuestion?: string;\n /**\n * Optional general description of what the query does\n */\n Description?: string;\n /**\n * Optional SQL query text to execute (can contain Nunjucks template syntax)\n */\n SQL?: string;\n /**\n * Optional technical documentation for developers\n */\n TechnicalDescription?: string;\n /**\n * Optional original SQL before optimization or modification\n */\n OriginalSQL?: string;\n /**\n * Optional user feedback about the query\n */\n Feedback?: string;\n /**\n * Optional query approval status (defaults to 'Pending')\n */\n Status?: 'Pending' | 'Approved' | 'Rejected' | 'Expired';\n /**\n * Optional quality indicator (higher = better quality, defaults to 0)\n */\n QualityRank?: number;\n /**\n * Optional execution cost indicator (higher = more expensive to run)\n */\n ExecutionCostRank?: number;\n /**\n * Optional flag indicating if the query uses Nunjucks template syntax (auto-detected if not specified)\n */\n UsesTemplate?: boolean;\n /**\n * Optional array of permissions to create for the query\n */\n Permissions?: QueryPermissionInput[];\n}\n\n/**\n * Type for query field information\n */\nexport interface QueryField {\n ID: string;\n QueryID: string;\n Name: string;\n Description?: string;\n Sequence: number;\n SQLBaseType?: string;\n SQLFullType?: string;\n SourceEntityID?: string;\n SourceEntity?: string;\n SourceFieldName?: string;\n IsComputed: boolean;\n ComputationDescription?: string;\n IsSummary?: boolean;\n SummaryDescription?: string;\n}\n\n/**\n * Type for query parameter information\n */\nexport interface QueryParameter {\n ID: string;\n QueryID: string;\n Name: string;\n Description?: string;\n Type: string;\n IsRequired: boolean;\n DefaultValue?: string;\n SampleValue?: string;\n ValidationFilters?: string;\n}\n\n/**\n * Type for query entity information\n */\nexport interface MJQueryEntity {\n ID: string;\n QueryID: string;\n EntityID: string;\n Entity?: string;\n}\n\n/**\n * Type for query permission information\n */\nexport interface QueryPermission {\n ID: string;\n QueryID: string;\n RoleID: string;\n Role?: string;\n}\n\n/**\n * Result type for CreateQuery mutation calls - contains creation success status and query data\n */\nexport interface CreateQueryResult {\n /**\n * Whether the query creation was successful\n */\n Success: boolean;\n /**\n * Error message if the creation failed (optional)\n */\n ErrorMessage?: string;\n /**\n * Unique identifier of the created query (optional)\n */\n ID?: string;\n /**\n * Display name of the created query (optional)\n */\n Name?: string;\n /**\n * Description of the created query (optional)\n */\n Description?: string;\n /**\n * Category ID the query belongs to (optional)\n */\n CategoryID?: string;\n /**\n * Category name the query belongs to (optional)\n */\n Category?: string;\n /**\n * SQL query text (optional)\n */\n SQL?: string;\n /**\n * Query status: Pending, Approved, Rejected, or Expired (optional)\n */\n Status?: string;\n /**\n * Quality rank indicator (optional)\n */\n QualityRank?: number;\n /**\n * Embedding vector for semantic search (optional)\n */\n EmbeddingVector?: string;\n /**\n * ID of the embedding model used (optional)\n */\n EmbeddingModelID?: string;\n /**\n * Name of the embedding model used (optional)\n */\n EmbeddingModelName?: string;\n /**\n * Array of fields discovered in the query (optional)\n */\n Fields?: QueryField[];\n /**\n * Array of parameters found in the query template (optional)\n */\n Parameters?: QueryParameter[];\n /**\n * Array of entities referenced by the query (optional)\n */\n Entities?: MJQueryEntity[];\n /**\n * Array of permissions created for the query (optional)\n */\n Permissions?: QueryPermission[];\n}\n\n/**\n * Input type for UpdateQuery mutation calls - updates an existing query\n */\nexport interface UpdateQueryInput {\n /**\n * Required ID of the query to update\n */\n ID: string;\n /**\n * Optional name for the query (must be unique within category)\n */\n Name?: string;\n /**\n * Optional category ID to move the query to\n */\n CategoryID?: string;\n /**\n * Optional category path for automatic hierarchy creation (e.g., \"Reports/Sales/Monthly\") - takes precedence over CategoryID\n */\n CategoryPath?: string;\n /**\n * Optional natural language question this query answers\n */\n UserQuestion?: string;\n /**\n * Optional general description of what the query does\n */\n Description?: string;\n /**\n * Optional SQL query text to execute (can contain Nunjucks template syntax)\n */\n SQL?: string;\n /**\n * Optional technical documentation for developers\n */\n TechnicalDescription?: string;\n /**\n * Optional original SQL before optimization or modification\n */\n OriginalSQL?: string;\n /**\n * Optional user feedback about the query\n */\n Feedback?: string;\n /**\n * Optional query approval status\n */\n Status?: 'Pending' | 'Approved' | 'Rejected' | 'Expired';\n /**\n * Optional quality indicator (higher = better quality)\n */\n QualityRank?: number;\n /**\n * Optional execution cost indicator (higher = more expensive to run)\n */\n ExecutionCostRank?: number;\n /**\n * Optional flag indicating if the query uses Nunjucks template syntax\n */\n UsesTemplate?: boolean;\n /**\n * Optional array of permissions to update for the query (replaces existing permissions)\n */\n Permissions?: QueryPermissionInput[];\n}\n\n/**\n * Result type for UpdateQuery mutation calls - contains update success status and query data\n */\nexport interface UpdateQueryResult {\n /**\n * Whether the query update was successful\n */\n Success: boolean;\n /**\n * Error message if the update failed (optional)\n */\n ErrorMessage?: string;\n /**\n * Unique identifier of the updated query (optional)\n */\n ID?: string;\n /**\n * Display name of the updated query (optional)\n */\n Name?: string;\n /**\n * Description of the updated query (optional)\n */\n Description?: string;\n /**\n * Category ID the query belongs to (optional)\n */\n CategoryID?: string;\n /**\n * Category name the query belongs to (optional)\n */\n Category?: string;\n /**\n * SQL query text (optional)\n */\n SQL?: string;\n /**\n * Query status: Pending, Approved, Rejected, or Expired (optional)\n */\n Status?: string;\n /**\n * Quality rank indicator (optional)\n */\n QualityRank?: number;\n /**\n * Embedding vector for semantic search (optional)\n */\n EmbeddingVector?: string;\n /**\n * ID of the embedding model used (optional)\n */\n EmbeddingModelID?: string;\n /**\n * Name of the embedding model used (optional)\n */\n EmbeddingModelName?: string;\n /**\n * Array of fields discovered in the query (optional)\n */\n Fields?: QueryField[];\n /**\n * Array of parameters found in the query template (optional)\n */\n Parameters?: QueryParameter[];\n /**\n * Array of entities referenced by the query (optional)\n */\n Entities?: MJQueryEntity[];\n /**\n * Array of permissions for the query (optional)\n */\n Permissions?: QueryPermission[];\n}\n\n/**\n * Delete options input type for controlling delete behavior.\n * All fields are optional - defaults will be applied if not provided.\n */\nexport interface DeleteQueryOptionsInput {\n /**\n * Whether to skip AI actions during deletion.\n * @default false\n */\n SkipEntityAIActions?: boolean;\n /**\n * Whether to skip regular entity actions during deletion.\n * @default false\n */\n SkipEntityActions?: boolean;\n /**\n * When true, bypasses Validate() and actual database deletion but still\n * invokes associated actions (AI Actions, Entity Actions, etc.).\n * Used for replaying/simulating delete operations.\n * @default false\n */\n ReplayOnly?: boolean;\n /**\n * When true, indicates this entity is being deleted as part of an IS-A parent chain\n * initiated by a child entity.\n * @default false\n */\n IsParentEntityDelete?: boolean;\n}\n\n/**\n * Result type for DeleteQuery mutation calls - contains deletion success status and deleted query data\n */\nexport interface DeleteQueryResult {\n /**\n * Whether the query deletion was successful\n */\n Success: boolean;\n /**\n * Error message if the deletion failed (optional)\n */\n ErrorMessage?: string;\n /**\n * Unique identifier of the deleted query (optional)\n */\n ID?: string;\n /**\n * Display name of the deleted query (optional)\n */\n Name?: string;\n}\n\n ","import { ActionParam, ActionResult, EntityActionInvocationParams, EntityActionResult } from \"@memberjunction/actions-base\";\nimport { CompositeKey, LogError } from \"@memberjunction/core\";\nimport { GraphQLDataProvider } from \"./graphQLDataProvider\";\nimport { gql } from \"graphql-request\";\n\n/**\n * Client for executing actions and entity actions through GraphQL.\n * This class provides an easy way to execute actions from a client application,\n * similar to how the ActionEngine and EntityActionEngine work on the server.\n * \n * The GraphQLActionClient follows the same naming convention as other GraphQL clients\n * in the MemberJunction ecosystem, such as GraphQLSystemUserClient.\n * \n * @example\n * ```typescript\n * // Create the client\n * const actionClient = new GraphQLActionClient(graphQLProvider);\n * \n * // Run a regular action\n * const result = await actionClient.RunAction(\"action-id\", [\n * { Name: \"parameter1\", Value: \"value1\", Type: \"Input\" }\n * ]);\n * \n * // Run an entity action\n * const entityActionResult = await actionClient.RunEntityAction({\n * EntityAction: action,\n * InvocationType: { Name: \"SingleRecord\" },\n * EntityObject: entityObject,\n * ContextUser: user\n * });\n * ```\n */\nexport class GraphQLActionClient {\n /**\n * The GraphQLDataProvider instance used to execute GraphQL requests\n * @private\n */\n private _dataProvider: GraphQLDataProvider;\n\n /**\n * Creates a new GraphQLActionClient instance.\n * @param dataProvider The GraphQL data provider to use for queries\n */\n constructor(dataProvider: GraphQLDataProvider) {\n this._dataProvider = dataProvider;\n }\n\n /**\n * Run an action by its ID with the specified parameters.\n * \n * This method invokes an action on the server through GraphQL and returns the result.\n * Action parameters are automatically serialized as needed, and results are deserialized\n * for complex data types.\n * \n * @param actionID The ID of the action to run\n * @param params Optional parameters to pass to the action\n * @param skipActionLog Whether to skip logging the action execution (defaults to false)\n * @returns A Promise that resolves to an ActionResult object containing the result of running the action\n * \n * @example\n * ```typescript\n * const result = await actionClient.RunAction(\"action-id\", [\n * { Name: \"param1\", Value: \"value1\", Type: \"Input\" }\n * ]);\n * \n * if (result.Success) {\n * // Action was successful\n * console.log(result.Message);\n * }\n * ```\n */\n public async RunAction(\n actionID: string, \n params?: ActionParam[], \n skipActionLog: boolean = false\n ): Promise<ActionResult> {\n try {\n // Prepare the input variables\n const serializedParams = this.serializeActionParameters(params);\n const variables = this.createActionVariables(actionID, serializedParams, skipActionLog);\n \n // Execute the mutation\n const result = await this.executeActionMutation(variables);\n \n // Process the result\n return this.processActionResult(result, params);\n } catch (e) {\n return this.handleActionError(e, params);\n }\n }\n\n /**\n * Serializes action parameters to ensure complex objects are properly JSON-encoded\n * @param params The action parameters to serialize\n * @returns The serialized parameters\n * @private\n */\n private serializeActionParameters(params?: ActionParam[]): any[] | undefined {\n if (!params) {\n return undefined;\n }\n\n return params.map(p => {\n let value = p.Value;\n if (value !== null && value !== undefined && typeof value === 'object') {\n value = JSON.stringify(value);\n }\n return {\n ...p,\n Value: value\n };\n });\n }\n\n /**\n * Creates the variables for the action mutation\n * @param actionID The ID of the action to run\n * @param params The serialized action parameters\n * @param skipActionLog Whether to skip action logging\n * @returns The action variables\n * @private\n */\n private createActionVariables(\n actionID: string, \n params?: any[], \n skipActionLog: boolean = false\n ): any {\n return {\n input: {\n ActionID: actionID,\n Params: params,\n SkipActionLog: skipActionLog\n }\n };\n }\n\n /**\n * Executes the action mutation\n * @param variables The variables for the mutation\n * @returns The result of the mutation\n * @private\n */\n private async executeActionMutation(variables: any): Promise<any> {\n const mutation = gql`\n mutation RunAction($input: RunActionInput!) {\n RunAction(input: $input) {\n Success\n Message\n ResultCode\n ResultData\n }\n }\n `;\n\n return await this._dataProvider.ExecuteGQL(mutation, variables);\n }\n\n /**\n * Processes the result of an action\n * @param result The result from the GraphQL query\n * @param originalParams The original parameters passed to the action\n * @returns The processed action result\n * @private\n */\n private processActionResult(result: any, originalParams?: ActionParam[]): ActionResult {\n if (!result?.RunAction) {\n throw new Error(\"Invalid response from server\");\n }\n\n // Parse the ResultData if it exists\n let resultData = undefined;\n try {\n if (result.RunAction.ResultData) {\n resultData = JSON.parse(result.RunAction.ResultData);\n }\n } catch (e) {\n LogError(`Failed to parse action result data: ${e}`);\n }\n\n // Return a properly formatted ActionResult\n return {\n Success: result.RunAction.Success,\n Message: result.RunAction.Message,\n Result: resultData,\n LogEntry: null, // We don't return the log entry to clients\n Params: originalParams || [],\n RunParams: null // We don't return the run params to clients\n };\n }\n\n /**\n * Handles errors in the action execution\n * @param e The error\n * @param originalParams The original parameters passed to the action\n * @returns An error result\n * @private\n */\n private handleActionError(e: unknown, originalParams?: ActionParam[]): ActionResult {\n const error = e as Error;\n LogError(`Error running action: ${error}`);\n return {\n Success: false,\n Message: `Error: ${error.message}`,\n Result: null,\n LogEntry: null,\n Params: originalParams || [],\n RunParams: null\n };\n }\n\n /**\n * Run an entity action with the specified parameters.\n * \n * This method invokes an entity action on the server through GraphQL and returns the result.\n * Entity actions are operations that can be performed on entity records, such as validation,\n * business logic, or custom processing. They can operate on a single record, a view, or a list.\n * \n * @param params The parameters for the entity action, including the action to run, \n * invocation type, entity object or view/list IDs, and optional parameters\n * @returns A Promise that resolves to an EntityActionResult object containing the result\n * \n * @example\n * ```typescript\n * // Run an entity action on a single record\n * const result = await actionClient.RunEntityAction({\n * EntityAction: action,\n * InvocationType: { Name: \"SingleRecord\" },\n * EntityObject: entityObject,\n * ContextUser: user\n * });\n * \n * // Run an entity action on a view\n * const viewResult = await actionClient.RunEntityAction({\n * EntityAction: action,\n * InvocationType: { Name: \"View\" },\n * ViewID: \"view-id\",\n * ContextUser: user\n * });\n * ```\n */\n public async RunEntityAction(params: EntityActionInvocationParams): Promise<EntityActionResult> {\n try {\n // Create the GraphQL input\n const input = this.createEntityActionInput(params);\n\n // Execute the GraphQL mutation\n const result = await this.executeEntityActionMutation(input);\n\n // Process the result\n return this.processEntityActionResult(result);\n } catch (e) {\n return this.handleEntityActionError(e);\n }\n }\n\n /**\n * Creates the GraphQL input for an entity action\n * @param params The entity action parameters\n * @returns The GraphQL input\n * @private\n */\n private createEntityActionInput(params: EntityActionInvocationParams): any {\n const input: any = {\n EntityActionID: params.EntityAction.ID,\n InvocationType: params.InvocationType.Name,\n ListID: params.ListID,\n ViewID: params.ViewID,\n };\n \n // Add parameters if available\n if ((params as any).Params) {\n input.Params = this.convertActionParams((params as any).Params);\n }\n \n // Add entity information if available\n if (params.EntityObject) {\n this.addEntityInformation(input, params.EntityObject);\n }\n\n return input;\n }\n\n /**\n * Converts action parameters to the format expected by the GraphQL API\n * @param params The action parameters\n * @returns The converted parameters\n * @private\n */\n private convertActionParams(params: any[]): any[] {\n return params.map(p => {\n let value = p.Value;\n if (value !== null && value !== undefined && typeof value === 'object') {\n value = JSON.stringify(value);\n }\n return {\n Name: p.Name,\n Value: value,\n Type: p.Type\n };\n });\n }\n\n /**\n * Adds entity information to the input object\n * @param input The input object to add to\n * @param entityObject The entity object\n * @private\n */\n private addEntityInformation(input: any, entityObject: any): void {\n // Prefer using entity name instead of ID for better code readability\n input.EntityName = entityObject.EntityInfo?.Name;\n \n // Convert the entity's primary key to the expected format\n if (entityObject.PrimaryKey) {\n input.PrimaryKey = this.convertPrimaryKey(entityObject.PrimaryKey);\n }\n }\n\n /**\n * Converts a primary key object to the format expected by the GraphQL API\n * @param primaryKey The primary key object\n * @returns The converted primary key\n * @private\n */\n private convertPrimaryKey(primaryKey: any): any {\n return {\n KeyValuePairs: primaryKey.KeyValuePairs.map(kvp => this.convertKeyValuePair(kvp))\n };\n }\n\n /**\n * Converts a key-value pair to a string format\n * @param kvp The key-value pair\n * @returns The converted key-value pair\n * @private\n */\n private convertKeyValuePair(kvp: any): any {\n return {\n FieldName: kvp.FieldName,\n Value: kvp.Value !== null && kvp.Value !== undefined ? \n (typeof kvp.Value === 'object' ? JSON.stringify(kvp.Value) : kvp.Value.toString()) \n : null\n };\n }\n\n /**\n * Executes the GraphQL mutation for an entity action\n * @param input The GraphQL input\n * @returns The GraphQL result\n * @private\n */\n private async executeEntityActionMutation(input: any): Promise<any> {\n const mutation = gql`\n mutation RunEntityAction($input: EntityActionInput!) {\n RunEntityAction(input: $input) {\n Success\n Message\n ResultData\n }\n }\n `;\n\n return await this._dataProvider.ExecuteGQL(mutation, { input });\n }\n\n /**\n * Processes the result of an entity action\n * @param result The GraphQL result\n * @returns The processed entity action result\n * @private\n */\n private processEntityActionResult(result: any): EntityActionResult {\n if (!result?.RunEntityAction) {\n throw new Error(\"Invalid response from server\");\n }\n\n // Parse the ResultData\n let resultData = {};\n try {\n if (result.RunEntityAction.ResultData) {\n resultData = JSON.parse(result.RunEntityAction.ResultData);\n }\n } catch (e) {\n LogError(`Failed to parse entity action result data: ${e}`);\n }\n\n // Return a properly formatted EntityActionResult\n return {\n Success: result.RunEntityAction.Success,\n Message: result.RunEntityAction.Message,\n RunParams: null, // We don't return run params to clients\n LogEntry: null, // We don't return the log entry to clients\n ...resultData\n };\n }\n\n /**\n * Handles errors in the entity action\n * @param e The error\n * @returns An error result\n * @private\n */\n private handleEntityActionError(e: unknown): EntityActionResult {\n const error = e as Error;\n LogError(`Error running entity action: ${error}`);\n return {\n Success: false,\n Message: `Error: ${error.message}`,\n RunParams: null,\n LogEntry: null\n };\n }\n}","import { LogError } from \"@memberjunction/core\";\nimport { GraphQLDataProvider } from \"./graphQLDataProvider\";\nimport { gql } from \"graphql-request\";\n\n/**\n * Result of creating an API key\n */\nexport interface CreateAPIKeyResult {\n /** Whether the operation succeeded */\n Success: boolean;\n /** The raw API key - show once and cannot be recovered */\n RawKey?: string;\n /** The database ID of the created key */\n APIKeyID?: string;\n /** Error message if operation failed */\n Error?: string;\n}\n\n/**\n * Parameters for creating an API key\n */\nexport interface CreateAPIKeyParams {\n /** Human-readable label for the key */\n Label: string;\n /** Optional description */\n Description?: string;\n /** Optional expiration date */\n ExpiresAt?: Date;\n /** Optional scope IDs to assign */\n ScopeIDs?: string[];\n}\n\n/**\n * Result of revoking an API key\n */\nexport interface RevokeAPIKeyResult {\n /** Whether the operation succeeded */\n Success: boolean;\n /** Error message if operation failed */\n Error?: string;\n}\n\n/**\n * Client for encryption-related GraphQL operations.\n *\n * This client provides methods for operations that require server-side\n * cryptographic processing, such as API key generation. These operations\n * cannot be performed client-side because they require secure random\n * number generation and cryptographic hashing that must match the\n * server's validation logic.\n *\n * @example\n * ```typescript\n * // Create the client\n * const encryptionClient = new GraphQLEncryptionClient(graphQLProvider);\n *\n * // Create a new API key\n * const result = await encryptionClient.CreateAPIKey({\n * Label: 'My Integration Key',\n * Description: 'Used for external service access',\n * ExpiresAt: new Date('2025-12-31'),\n * ScopeIDs: ['scope-id-1', 'scope-id-2']\n * });\n *\n * if (result.Success) {\n * // Show rawKey to user ONCE - cannot be recovered\n * console.log('Save this key:', result.RawKey);\n * }\n * ```\n */\nexport class GraphQLEncryptionClient {\n /**\n * The GraphQLDataProvider instance used to execute GraphQL requests\n */\n private _dataProvider: GraphQLDataProvider;\n\n /**\n * Creates a new GraphQLEncryptionClient instance.\n * @param dataProvider The GraphQL data provider to use for queries\n */\n constructor(dataProvider: GraphQLDataProvider) {\n this._dataProvider = dataProvider;\n }\n\n /**\n * Creates a new API key with secure server-side cryptographic hashing.\n *\n * This method calls the server to:\n * 1. Generate a cryptographically secure random API key\n * 2. Hash the key using SHA-256 for secure storage\n * 3. Store only the hash in the database\n * 4. Return the raw key ONCE\n *\n * **CRITICAL**: The raw key is returned only once and cannot be recovered.\n * Instruct users to save it immediately in a secure location.\n *\n * @param params Configuration for the new API key\n * @returns Result with raw key (show once!) and database ID\n *\n * @example\n * ```typescript\n * const result = await client.CreateAPIKey({\n * Label: 'Production Integration',\n * Description: 'API access for our CRM system',\n * ExpiresAt: new Date('2025-12-31'),\n * ScopeIDs: ['entities:read', 'entities:write']\n * });\n *\n * if (result.Success) {\n * alert(`Save this key now! It won't be shown again:\\n${result.RawKey}`);\n * } else {\n * console.error('Failed to create key:', result.Error);\n * }\n * ```\n */\n public async CreateAPIKey(params: CreateAPIKeyParams): Promise<CreateAPIKeyResult> {\n try {\n const variables = this.createAPIKeyVariables(params);\n const result = await this.executeCreateAPIKeyMutation(variables);\n return this.processCreateAPIKeyResult(result);\n } catch (e) {\n return this.handleCreateAPIKeyError(e);\n }\n }\n\n /**\n * Creates the variables for the CreateAPIKey mutation\n * @param params The API key creation parameters\n * @returns The mutation variables\n */\n private createAPIKeyVariables(params: CreateAPIKeyParams): Record<string, unknown> {\n return {\n input: {\n Label: params.Label,\n Description: params.Description,\n ExpiresAt: params.ExpiresAt?.toISOString(),\n ScopeIDs: params.ScopeIDs\n }\n };\n }\n\n /**\n * Executes the CreateAPIKey mutation\n * @param variables The mutation variables\n * @returns The GraphQL result\n */\n private async executeCreateAPIKeyMutation(variables: Record<string, unknown>): Promise<Record<string, unknown>> {\n const mutation = gql`\n mutation CreateAPIKey($input: CreateAPIKeyInput!) {\n CreateAPIKey(input: $input) {\n Success\n RawKey\n APIKeyID\n Error\n }\n }\n `;\n\n return await this._dataProvider.ExecuteGQL(mutation, variables);\n }\n\n /**\n * Processes the result of the CreateAPIKey mutation\n * @param result The GraphQL result\n * @returns The processed result\n */\n private processCreateAPIKeyResult(result: Record<string, unknown>): CreateAPIKeyResult {\n const data = result as { CreateAPIKey?: CreateAPIKeyResult };\n\n if (!data?.CreateAPIKey) {\n return {\n Success: false,\n Error: \"Invalid response from server\"\n };\n }\n\n return {\n Success: data.CreateAPIKey.Success,\n RawKey: data.CreateAPIKey.RawKey,\n APIKeyID: data.CreateAPIKey.APIKeyID,\n Error: data.CreateAPIKey.Error\n };\n }\n\n /**\n * Handles errors in the CreateAPIKey operation\n * @param e The error\n * @returns An error result\n */\n private handleCreateAPIKeyError(e: unknown): CreateAPIKeyResult {\n const error = e as Error;\n LogError(`Error creating API key: ${error.message}`);\n return {\n Success: false,\n Error: `Error: ${error.message}`\n };\n }\n\n /**\n * Revokes an API key, permanently disabling it.\n *\n * Once revoked, an API key cannot be reactivated. Users must create a new key.\n *\n * @param apiKeyId The database ID of the API key to revoke\n * @returns Result indicating success or failure\n *\n * @example\n * ```typescript\n * const result = await client.RevokeAPIKey('key-uuid-here');\n *\n * if (result.Success) {\n * console.log('API key has been revoked');\n * } else {\n * console.error('Failed to revoke:', result.Error);\n * }\n * ```\n */\n public async RevokeAPIKey(apiKeyId: string): Promise<RevokeAPIKeyResult> {\n try {\n const mutation = gql`\n mutation RevokeAPIKey($apiKeyId: String!) {\n RevokeAPIKey(apiKeyId: $apiKeyId) {\n Success\n Error\n }\n }\n `;\n\n const result = await this._dataProvider.ExecuteGQL(mutation, { apiKeyId });\n const data = result as { RevokeAPIKey?: RevokeAPIKeyResult };\n\n if (!data?.RevokeAPIKey) {\n return {\n Success: false,\n Error: \"Invalid response from server\"\n };\n }\n\n return {\n Success: data.RevokeAPIKey.Success,\n Error: data.RevokeAPIKey.Error\n };\n } catch (e) {\n const error = e as Error;\n LogError(`Error revoking API key: ${error.message}`);\n return {\n Success: false,\n Error: `Error: ${error.message}`\n };\n }\n }\n}\n","import { LogError } from \"@memberjunction/core\";\nimport { GraphQLDataProvider } from \"./graphQLDataProvider\";\nimport { gql } from \"graphql-request\";\nimport { SafeJSONParse } from \"@memberjunction/global\";\n\n/**\n * Parameters for running a test\n */\nexport interface RunTestParams {\n testId: string;\n verbose?: boolean;\n environment?: string;\n tags?: string[];\n /**\n * Variable values to use for this test run.\n * Key is the variable name, value is the resolved value.\n */\n variables?: Record<string, unknown>;\n onProgress?: (progress: TestExecutionProgress) => void;\n}\n\n/**\n * Result from running a test\n */\nexport interface RunTestResult {\n success: boolean;\n errorMessage?: string;\n executionTimeMs?: number;\n result: any; // Parsed TestRunResult from engine\n}\n\n/**\n * Parameters for running a test suite\n */\nexport interface RunTestSuiteParams {\n suiteId: string;\n verbose?: boolean;\n environment?: string;\n parallel?: boolean;\n tags?: string[];\n /**\n * Variable values to apply to all tests in this suite.\n * Key is the variable name, value is the resolved value.\n */\n variables?: Record<string, unknown>;\n /**\n * Run only specific tests by their IDs.\n * If provided, only tests with matching IDs will be executed.\n */\n selectedTestIds?: string[];\n /**\n * Start execution from this sequence number (inclusive).\n * Tests with sequence numbers less than this value will be skipped.\n */\n sequenceStart?: number;\n /**\n * Stop execution at this sequence number (inclusive).\n * Tests with sequence numbers greater than this value will be skipped.\n */\n sequenceEnd?: number;\n onProgress?: (progress: TestExecutionProgress) => void;\n}\n\n/**\n * Result from running a test suite\n */\nexport interface RunTestSuiteResult {\n success: boolean;\n errorMessage?: string;\n executionTimeMs?: number;\n result: any; // Parsed TestSuiteRunResult from engine\n}\n\n/**\n * Test execution progress update\n */\nexport interface TestExecutionProgress {\n currentStep: string;\n percentage: number;\n message: string;\n testName?: string;\n driverType?: string;\n oracleEvaluation?: string;\n}\n\n/**\n * Client for executing tests through GraphQL.\n * This class provides an easy way to run tests and test suites from a client application.\n *\n * @example\n * ```typescript\n * // Create the client\n * const testingClient = new GraphQLTestingClient(graphQLProvider);\n *\n * // Run a test\n * const result = await testingClient.RunTest({\n * testId: \"test-uuid\",\n * verbose: true,\n * environment: \"dev\"\n * });\n *\n * // Run a test suite\n * const suiteResult = await testingClient.RunTestSuite({\n * suiteId: \"suite-uuid\",\n * parallel: true\n * });\n * ```\n */\nexport class GraphQLTestingClient {\n private _dataProvider: GraphQLDataProvider;\n\n /**\n * Creates a new GraphQLTestingClient instance.\n * @param dataProvider The GraphQL data provider to use for queries\n */\n constructor(dataProvider: GraphQLDataProvider) {\n this._dataProvider = dataProvider;\n }\n\n /**\n * Run a single test with the specified parameters.\n *\n * This method invokes a test on the server through GraphQL and returns the result.\n * If a progress callback is provided in params.onProgress, this method will subscribe\n * to real-time progress updates from the GraphQL server and forward them to the callback.\n *\n * @param params The parameters for running the test\n * @returns A Promise that resolves to a RunTestResult object\n *\n * @example\n * ```typescript\n * const result = await testingClient.RunTest({\n * testId: \"test-uuid\",\n * verbose: true,\n * environment: \"staging\",\n * onProgress: (progress) => {\n * console.log(`${progress.currentStep}: ${progress.message} (${progress.percentage}%)`);\n * }\n * });\n *\n * if (result.success) {\n * console.log('Test passed!', result.result);\n * } else {\n * console.error('Test failed:', result.errorMessage);\n * }\n * ```\n */\n public async RunTest(params: RunTestParams): Promise<RunTestResult> {\n let subscription: any;\n\n try {\n // Subscribe to progress updates if callback provided\n if (params.onProgress) {\n subscription = this._dataProvider.PushStatusUpdates(this._dataProvider.sessionId)\n .subscribe((message: string) => {\n try {\n const parsed = JSON.parse(message);\n\n // Filter for TestExecutionProgress messages from RunTestResolver\n if (parsed.resolver === 'RunTestResolver' &&\n parsed.type === 'TestExecutionProgress' &&\n parsed.status === 'ok' &&\n parsed.data?.progress) {\n\n // Forward progress to callback\n params.onProgress!(parsed.data.progress);\n }\n } catch (e) {\n console.error('[GraphQLTestingClient] Failed to parse progress message:', e);\n }\n });\n }\n\n const mutation = gql`\n mutation RunTest(\n $testId: String!,\n $verbose: Boolean,\n $environment: String,\n $tags: String,\n $variables: String\n ) {\n RunTest(\n testId: $testId,\n verbose: $verbose,\n environment: $environment,\n tags: $tags,\n variables: $variables\n ) {\n success\n errorMessage\n executionTimeMs\n result\n }\n }\n `;\n\n // Serialize tags array to JSON string for GraphQL\n const tagsJson = params.tags && params.tags.length > 0 ? JSON.stringify(params.tags) : undefined;\n // Serialize variables object to JSON string for GraphQL\n const variablesJson = params.variables ? JSON.stringify(params.variables) : undefined;\n\n const variables = {\n testId: params.testId,\n verbose: params.verbose,\n environment: params.environment,\n tags: tagsJson,\n variables: variablesJson\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n return this.processTestResult(result.RunTest);\n\n } catch (e) {\n return this.handleError(e, 'RunTest');\n } finally {\n // Always clean up subscription\n if (subscription) {\n subscription.unsubscribe();\n }\n }\n }\n\n /**\n * Run a test suite with the specified parameters.\n *\n * If a progress callback is provided in params.onProgress, this method will subscribe\n * to real-time progress updates from the GraphQL server and forward them to the callback.\n *\n * @param params The parameters for running the test suite\n * @returns A Promise that resolves to a RunTestSuiteResult object\n *\n * @example\n * ```typescript\n * const result = await testingClient.RunTestSuite({\n * suiteId: \"suite-uuid\",\n * parallel: true,\n * verbose: false,\n * onProgress: (progress) => {\n * console.log(`Progress: ${progress.message} (${progress.percentage}%)`);\n * }\n * });\n *\n * console.log(`Suite: ${result.result.totalTests} tests run`);\n * console.log(`Passed: ${result.result.passedTests}`);\n * ```\n */\n public async RunTestSuite(params: RunTestSuiteParams): Promise<RunTestSuiteResult> {\n let subscription: any;\n\n try {\n // Subscribe to progress updates if callback provided\n if (params.onProgress) {\n subscription = this._dataProvider.PushStatusUpdates(this._dataProvider.sessionId)\n .subscribe((message: string) => {\n try {\n const parsed = JSON.parse(message);\n\n // Filter for TestExecutionProgress messages from RunTestResolver\n if (parsed.resolver === 'RunTestResolver' &&\n parsed.type === 'TestExecutionProgress' &&\n parsed.status === 'ok' &&\n parsed.data?.progress) {\n\n // Forward progress to callback\n params.onProgress!(parsed.data.progress);\n }\n } catch (e) {\n console.error('[GraphQLTestingClient] Failed to parse progress message:', e);\n }\n });\n }\n\n const mutation = gql`\n mutation RunTestSuite(\n $suiteId: String!,\n $verbose: Boolean,\n $environment: String,\n $parallel: Boolean,\n $tags: String,\n $variables: String,\n $selectedTestIds: String,\n $sequenceStart: Int,\n $sequenceEnd: Int\n ) {\n RunTestSuite(\n suiteId: $suiteId,\n verbose: $verbose,\n environment: $environment,\n parallel: $parallel,\n tags: $tags,\n variables: $variables,\n selectedTestIds: $selectedTestIds,\n sequenceStart: $sequenceStart,\n sequenceEnd: $sequenceEnd\n ) {\n success\n errorMessage\n executionTimeMs\n result\n }\n }\n `;\n\n // Serialize tags array to JSON string for GraphQL\n const tagsJson = params.tags && params.tags.length > 0 ? JSON.stringify(params.tags) : undefined;\n // Serialize variables object to JSON string for GraphQL\n const variablesJson = params.variables ? JSON.stringify(params.variables) : undefined;\n // Serialize selectedTestIds array to JSON string for GraphQL\n const selectedTestIdsJson = params.selectedTestIds && params.selectedTestIds.length > 0\n ? JSON.stringify(params.selectedTestIds)\n : undefined;\n\n const variables = {\n suiteId: params.suiteId,\n verbose: params.verbose,\n environment: params.environment,\n parallel: params.parallel,\n tags: tagsJson,\n variables: variablesJson,\n selectedTestIds: selectedTestIdsJson,\n sequenceStart: params.sequenceStart,\n sequenceEnd: params.sequenceEnd\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n return this.processSuiteResult(result.RunTestSuite);\n\n } catch (e) {\n return this.handleError(e, 'RunTestSuite');\n } finally {\n // Always clean up subscription\n if (subscription) {\n subscription.unsubscribe();\n }\n }\n }\n\n /**\n * Check if a test is currently running\n *\n * @param testId The test ID to check\n * @returns True if the test is running, false otherwise\n */\n public async IsTestRunning(testId: string): Promise<boolean> {\n try {\n const query = gql`\n query IsTestRunning($testId: String!) {\n IsTestRunning(testId: $testId)\n }\n `;\n\n const result = await this._dataProvider.ExecuteGQL(query, { testId });\n return result.IsTestRunning;\n\n } catch (e) {\n LogError(`Error checking test running status: ${(e as Error).message}`);\n return false;\n }\n }\n\n // ===== Helper Methods =====\n\n private processTestResult(result: any): RunTestResult {\n if (!result) {\n throw new Error(\"Invalid response from server\");\n }\n\n let parsedResult: any;\n try {\n parsedResult = SafeJSONParse(result.result);\n } catch (e) {\n parsedResult = result.result;\n }\n\n return {\n success: result.success,\n errorMessage: result.errorMessage,\n executionTimeMs: result.executionTimeMs,\n result: parsedResult\n };\n }\n\n private processSuiteResult(result: any): RunTestSuiteResult {\n if (!result) {\n throw new Error(\"Invalid response from server\");\n }\n\n let parsedResult: any;\n try {\n parsedResult = SafeJSONParse(result.result);\n } catch (e) {\n parsedResult = result.result;\n }\n\n return {\n success: result.success,\n errorMessage: result.errorMessage,\n executionTimeMs: result.executionTimeMs,\n result: parsedResult\n };\n }\n\n private handleError(error: any, operation: string): any {\n const errorMsg = (error as Error).message;\n LogError(`${operation} failed: ${errorMsg}`);\n\n return {\n success: false,\n errorMessage: errorMsg,\n result: null\n };\n }\n}\n","import { LogError } from \"@memberjunction/core\";\nimport { GraphQLDataProvider } from \"./graphQLDataProvider\";\nimport { gql } from \"graphql-request\";\nimport { ComponentSpec } from \"@memberjunction/interactive-component-types\";\n\n/**\n * Parameters for getting a component from registry\n */\nexport interface GetRegistryComponentParams {\n /**\n * Registry name (globally unique)\n */\n registryName: string;\n \n /**\n * Component namespace\n */\n namespace: string;\n \n /**\n * Component name\n */\n name: string;\n \n /**\n * Component version (optional, defaults to 'latest')\n */\n version?: string;\n \n /**\n * Optional hash for caching - if provided and matches, returns null\n */\n hash?: string;\n}\n\n/**\n * Response from GetRegistryComponent with hash and caching metadata\n */\nexport interface ComponentSpecWithHash {\n /**\n * The component specification (undefined if not modified)\n */\n specification?: ComponentSpec | string; // Can be either parsed object or JSON string\n \n /**\n * SHA-256 hash of the specification\n */\n hash: string;\n \n /**\n * Indicates if the component was not modified (304 response)\n */\n notModified: boolean;\n \n /**\n * Optional message from server\n */\n message?: string;\n}\n\n/**\n * Parameters for searching registry components\n */\nexport interface SearchRegistryComponentsParams {\n /**\n * Optional registry name filter\n */\n registryName?: string;\n \n /**\n * Optional namespace filter\n */\n namespace?: string;\n \n /**\n * Search query string\n */\n query?: string;\n \n /**\n * Component type filter\n */\n type?: string;\n \n /**\n * Tags to filter by\n */\n tags?: string[];\n \n /**\n * Maximum number of results\n */\n limit?: number;\n \n /**\n * Offset for pagination\n */\n offset?: number;\n}\n\n/**\n * Search result for registry components\n */\nexport interface RegistryComponentSearchResult {\n /**\n * Array of matching components\n */\n components: ComponentSpec[];\n \n /**\n * Total number of matches\n */\n total: number;\n \n /**\n * Current offset\n */\n offset: number;\n \n /**\n * Current limit\n */\n limit: number;\n}\n\n/**\n * Dependency tree for a component\n */\nexport interface ComponentDependencyTree {\n /**\n * Component ID\n */\n componentId: string;\n\n /**\n * Component name\n */\n name?: string;\n\n /**\n * Component namespace\n */\n namespace?: string;\n\n /**\n * Component version\n */\n version?: string;\n\n /**\n * Direct dependencies\n */\n dependencies?: ComponentDependencyTree[];\n\n /**\n * Whether this is a circular dependency\n */\n circular?: boolean;\n\n /**\n * Total count of all dependencies\n */\n totalCount?: number;\n}\n\n/**\n * Input parameters for sending component feedback\n */\nexport interface ComponentFeedbackParams {\n /**\n * Component name\n */\n componentName: string;\n\n /**\n * Component namespace\n */\n componentNamespace: string;\n\n /**\n * Component version (optional)\n */\n componentVersion?: string;\n\n /**\n * Registry name (optional - for registry-specific feedback)\n */\n registryName?: string;\n\n /**\n * Rating (typically 0-5 scale)\n */\n rating: number;\n\n /**\n * Type of feedback (optional)\n */\n feedbackType?: string;\n\n /**\n * User comments (optional)\n */\n comments?: string;\n\n /**\n * Associated conversation ID (optional)\n */\n conversationID?: string;\n\n /**\n * Associated conversation detail ID (optional)\n */\n conversationDetailID?: string;\n\n /**\n * Associated report ID (optional)\n */\n reportID?: string;\n\n /**\n * Associated dashboard ID (optional)\n */\n dashboardID?: string;\n}\n\n/**\n * Response from sending component feedback\n */\nexport interface ComponentFeedbackResponse {\n /**\n * Whether the feedback was successfully submitted\n */\n success: boolean;\n\n /**\n * ID of the created feedback record (if available)\n */\n feedbackID?: string;\n\n /**\n * Error message if submission failed\n */\n error?: string;\n}\n\n/**\n * Client for executing Component Registry operations through GraphQL.\n * This class provides an easy way to fetch components from external registries\n * through the MJ API server, which handles authentication and caching.\n * \n * The GraphQLComponentRegistryClient follows the same naming convention as other GraphQL clients\n * in the MemberJunction ecosystem, such as GraphQLAIClient and GraphQLActionClient.\n * \n * @example\n * ```typescript\n * // Create the client\n * const registryClient = new GraphQLComponentRegistryClient(graphQLProvider);\n * \n * // Get a component from a registry\n * const component = await registryClient.GetRegistryComponent({\n * registryName: \"MJ\",\n * namespace: \"core/ui\",\n * name: \"DataGrid\",\n * version: \"1.0.0\"\n * });\n * \n * // Search for components\n * const searchResult = await registryClient.SearchRegistryComponents({\n * query: \"dashboard\",\n * type: \"dashboard\",\n * limit: 10\n * });\n * ```\n */\nexport class GraphQLComponentRegistryClient {\n /**\n * The GraphQLDataProvider instance used to execute GraphQL requests\n * @private\n */\n private _dataProvider: GraphQLDataProvider;\n\n /**\n * Creates a new GraphQLComponentRegistryClient instance.\n * @param dataProvider The GraphQL data provider to use for queries\n */\n constructor(dataProvider: GraphQLDataProvider) {\n this._dataProvider = dataProvider;\n }\n\n /**\n * Get a specific component from a registry.\n * \n * This method fetches a component specification from an external registry\n * through the MJ API server. The server handles authentication with the\n * registry and may cache the result for performance.\n * \n * @param params The parameters for getting the component\n * @returns A Promise that resolves to a ComponentSpec\n * \n * @example\n * ```typescript\n * const component = await registryClient.GetRegistryComponent({\n * registryName: \"MJ\",\n * namespace: \"core/ui\",\n * name: \"DataGrid\",\n * version: \"2.0.0\"\n * });\n * \n * console.log('Component:', component.name);\n * console.log('Description:', component.description);\n * console.log('Code:', component.code);\n * ```\n */\n public async GetRegistryComponent(params: GetRegistryComponentParams): Promise<ComponentSpec | null> {\n try {\n // Build the query - specification is now a JSON string\n const query = gql`\n query GetRegistryComponent(\n $registryName: String!,\n $namespace: String!,\n $name: String!,\n $version: String,\n $hash: String\n ) {\n GetRegistryComponent(\n registryName: $registryName,\n namespace: $namespace,\n name: $name,\n version: $version,\n hash: $hash\n ) {\n hash\n notModified\n message\n specification\n }\n }\n `;\n\n // Prepare variables\n const variables: Record<string, any> = {\n registryName: params.registryName,\n namespace: params.namespace,\n name: params.name\n };\n\n if (params.version !== undefined) {\n variables.version = params.version;\n }\n \n if (params.hash !== undefined) {\n variables.hash = params.hash;\n }\n\n // Execute the query\n const result = await this._dataProvider.ExecuteGQL(query, variables);\n\n // Handle new response structure with hash\n if (result && result.GetRegistryComponent) {\n const response = result.GetRegistryComponent as ComponentSpecWithHash;\n \n // If not modified and no specification, return null (client should use cache)\n if (response.notModified && !response.specification) {\n return null;\n }\n \n // Parse the JSON string specification if available\n if (response.specification) {\n // If it's already an object, return it\n if (typeof response.specification === 'object') {\n return response.specification as ComponentSpec;\n }\n // Otherwise parse the JSON string\n try {\n return JSON.parse(response.specification) as ComponentSpec;\n } catch (e) {\n LogError(`Failed to parse component specification: ${e}`);\n return null;\n }\n }\n \n return null;\n }\n\n return null;\n } catch (e) {\n LogError(e);\n throw new Error(`Failed to get registry component: ${e instanceof Error ? e.message : 'Unknown error'}`);\n }\n }\n\n /**\n * Get a component from registry with hash and caching metadata.\n * Returns the full response including hash and notModified flag.\n * \n * @param params - Parameters for fetching the component\n * @returns Full response with specification, hash, and caching metadata\n * \n * @example\n * ```typescript\n * const response = await client.GetRegistryComponentWithHash({\n * registryName: 'MJ',\n * namespace: 'core/ui',\n * name: 'DataGrid',\n * version: '1.0.0',\n * hash: 'abc123...'\n * });\n * \n * if (response.notModified) {\n * // Use cached version\n * } else {\n * // Use response.specification\n * }\n * ```\n */\n public async GetRegistryComponentWithHash(params: GetRegistryComponentParams): Promise<ComponentSpecWithHash> {\n try {\n // Build the query - same as GetRegistryComponent\n const query = gql`\n query GetRegistryComponent(\n $registryName: String!,\n $namespace: String!,\n $name: String!,\n $version: String,\n $hash: String\n ) {\n GetRegistryComponent(\n registryName: $registryName,\n namespace: $namespace,\n name: $name,\n version: $version,\n hash: $hash\n ) {\n hash\n notModified\n message\n specification\n }\n }\n `;\n\n // Prepare variables\n const variables: Record<string, any> = {\n registryName: params.registryName,\n namespace: params.namespace,\n name: params.name\n };\n\n if (params.version !== undefined) {\n variables.version = params.version;\n }\n \n if (params.hash !== undefined) {\n variables.hash = params.hash;\n }\n\n // Execute the query\n const result = await this._dataProvider.ExecuteGQL(query, variables);\n\n // Return the full response with parsed specification\n if (result && result.GetRegistryComponent) {\n const response = result.GetRegistryComponent;\n let spec: ComponentSpec | undefined;\n if (response.specification) {\n try {\n spec = JSON.parse(response.specification) as ComponentSpec;\n } catch (e) {\n LogError(`Failed to parse component specification in GetRegistryComponentWithHash: ${e}`);\n spec = undefined;\n }\n }\n return {\n specification: spec,\n hash: response.hash,\n notModified: response.notModified,\n message: response.message\n } as ComponentSpecWithHash;\n }\n\n // Return empty response if nothing found\n return {\n specification: undefined,\n hash: '',\n notModified: false,\n message: 'Component not found'\n };\n } catch (e) {\n LogError(e);\n throw new Error(`Failed to get registry component with hash: ${e instanceof Error ? e.message : 'Unknown error'}`);\n }\n }\n\n /**\n * Search for components in registries.\n * \n * This method searches for components across one or more registries\n * based on the provided criteria. Results are paginated for performance.\n * \n * @param params The search parameters\n * @returns A Promise that resolves to a RegistryComponentSearchResult\n * \n * @example\n * ```typescript\n * const searchResult = await registryClient.SearchRegistryComponents({\n * query: \"dashboard\",\n * type: \"dashboard\",\n * tags: [\"analytics\", \"reporting\"],\n * limit: 20,\n * offset: 0\n * });\n * \n * console.log(`Found ${searchResult.total} components`);\n * searchResult.components.forEach(component => {\n * console.log(`- ${component.name}: ${component.description}`);\n * });\n * ```\n */\n public async SearchRegistryComponents(params: SearchRegistryComponentsParams): Promise<RegistryComponentSearchResult> {\n try {\n // Build the query\n const query = gql`\n query SearchRegistryComponents($params: SearchRegistryComponentsInput!) {\n SearchRegistryComponents(params: $params) {\n components\n total\n offset\n limit\n }\n }\n `;\n\n // Execute the query\n const result = await this._dataProvider.ExecuteGQL(query, { params });\n\n // Return the search result with parsed components\n if (result && result.SearchRegistryComponents) {\n const searchResult = result.SearchRegistryComponents;\n return {\n components: searchResult.components.map((json: string) => JSON.parse(json) as ComponentSpec),\n total: searchResult.total,\n offset: searchResult.offset,\n limit: searchResult.limit\n } as RegistryComponentSearchResult;\n }\n\n return {\n components: [],\n total: 0,\n offset: 0,\n limit: params.limit || 10\n };\n } catch (e) {\n LogError(e);\n throw new Error(`Failed to search registry components: ${e instanceof Error ? e.message : 'Unknown error'}`);\n }\n }\n\n /**\n * Resolve the dependency tree for a component.\n * \n * This method fetches the complete dependency tree for a component,\n * including all transitive dependencies. The server handles circular\n * dependency detection and marks them appropriately.\n * \n * @param registryId The registry ID\n * @param componentId The component ID\n * @returns A Promise that resolves to a ComponentDependencyTree\n * \n * @example\n * ```typescript\n * const dependencyTree = await registryClient.ResolveComponentDependencies(\n * \"mj-central\",\n * \"component-123\"\n * );\n * \n * console.log(`Component has ${dependencyTree.totalCount} total dependencies`);\n * if (dependencyTree.circular) {\n * console.warn('Circular dependency detected!');\n * }\n * ```\n */\n public async ResolveComponentDependencies(\n registryId: string,\n componentId: string\n ): Promise<ComponentDependencyTree | null> {\n try {\n // Build the query\n const query = gql`\n query ResolveComponentDependencies(\n $registryId: String!,\n $componentId: String!\n ) {\n ResolveComponentDependencies(\n registryId: $registryId,\n componentId: $componentId\n ) {\n componentId\n name\n namespace\n version\n circular\n totalCount\n dependencies {\n componentId\n name\n namespace\n version\n circular\n totalCount\n }\n }\n }\n `;\n\n // Execute the query\n const result = await this._dataProvider.ExecuteGQL(query, {\n registryId,\n componentId\n });\n\n // Return the dependency tree\n if (result && result.ResolveComponentDependencies) {\n return result.ResolveComponentDependencies as ComponentDependencyTree;\n }\n\n return null;\n } catch (e) {\n LogError(e);\n throw new Error(`Failed to resolve component dependencies: ${e instanceof Error ? e.message : 'Unknown error'}`);\n }\n }\n\n /**\n * Check if a specific version of a component exists in a registry.\n * \n * @param params The parameters for checking component existence\n * @returns A Promise that resolves to true if the component exists, false otherwise\n * \n * @example\n * ```typescript\n * const exists = await registryClient.ComponentExists({\n * registryId: \"mj-central\",\n * namespace: \"core/ui\",\n * name: \"DataGrid\",\n * version: \"2.0.0\"\n * });\n * \n * if (exists) {\n * console.log('Component is available');\n * }\n * ```\n */\n public async ComponentExists(params: GetRegistryComponentParams): Promise<boolean> {\n try {\n const component = await this.GetRegistryComponent(params);\n return component !== null;\n } catch (e) {\n // If we get an error, assume the component doesn't exist\n return false;\n }\n }\n\n /**\n * Get the latest version of a component.\n * \n * @param registryId The registry ID\n * @param namespace The component namespace\n * @param name The component name\n * @returns A Promise that resolves to the latest version string or null\n * \n * @example\n * ```typescript\n * const latestVersion = await registryClient.GetLatestVersion(\n * \"mj-central\",\n * \"core/ui\",\n * \"DataGrid\"\n * );\n * \n * console.log(`Latest version: ${latestVersion}`);\n * ```\n */\n public async GetLatestVersion(\n registryName: string,\n namespace: string,\n name: string\n ): Promise<string | null> {\n try {\n const component = await this.GetRegistryComponent({\n registryName,\n namespace,\n name,\n version: 'latest'\n });\n\n return component?.version || null;\n } catch (e) {\n LogError(e);\n return null;\n }\n }\n\n /**\n * Send feedback for a component.\n *\n * This is a registry-agnostic method that allows submitting feedback\n * for any component from any registry. The feedback can include ratings,\n * comments, and associations with conversations, reports, or dashboards.\n *\n * @param params The feedback parameters\n * @returns A Promise that resolves to a ComponentFeedbackResponse\n *\n * @example\n * ```typescript\n * const response = await registryClient.SendComponentFeedback({\n * componentName: 'DataGrid',\n * componentNamespace: 'core/ui',\n * componentVersion: '1.0.0',\n * registryName: 'MJ',\n * rating: 5,\n * feedbackType: 'feature-request',\n * comments: 'Would love to see export to Excel functionality',\n * conversationID: 'conv-123'\n * });\n *\n * if (response.success) {\n * console.log('Feedback submitted successfully!');\n * if (response.feedbackID) {\n * console.log(`Feedback ID: ${response.feedbackID}`);\n * }\n * } else {\n * console.error('Feedback submission failed:', response.error);\n * }\n * ```\n */\n public async SendComponentFeedback(params: ComponentFeedbackParams): Promise<ComponentFeedbackResponse> {\n try {\n // Build the mutation\n const mutation = gql`\n mutation SendComponentFeedback($feedback: ComponentFeedbackInput!) {\n SendComponentFeedback(feedback: $feedback) {\n success\n feedbackID\n error\n }\n }\n `;\n\n // Execute the mutation\n const result = await this._dataProvider.ExecuteGQL(mutation, { feedback: params });\n\n // Return the response\n if (result && result.SendComponentFeedback) {\n return result.SendComponentFeedback as ComponentFeedbackResponse;\n }\n\n return {\n success: false,\n error: 'No response from server'\n };\n } catch (e) {\n LogError(e);\n return {\n success: false,\n error: e instanceof Error ? e.message : 'Unknown error'\n };\n }\n }\n}","import { LogError } from '@memberjunction/core';\nimport { GraphQLDataProvider } from './graphQLDataProvider';\nimport { gql } from 'graphql-request';\n\n// =========================================================================\n// Client-side params / result types\n// =========================================================================\n\n/**\n * Progress update received during label creation.\n */\nexport interface CreateVersionLabelProgress {\n /** Current lifecycle step */\n Step: 'initializing' | 'walking_dependencies' | 'capturing_snapshots' | 'finalizing';\n /** Human-readable description of what's happening */\n Message: string;\n /** Estimated completion percentage (0–100) */\n Percentage: number;\n /** Number of records processed so far */\n RecordsProcessed?: number;\n /** Total records to process */\n TotalRecords?: number;\n /** Entity currently being processed */\n CurrentEntity?: string;\n}\n\n/**\n * Parameters for creating a version label via the server-side VersionHistoryEngine.\n */\nexport interface CreateVersionLabelParams {\n /** Human-readable name */\n Name: string;\n /** Optional longer description */\n Description?: string;\n /** Scope of the label: System, Entity, or Record (default: Record) */\n Scope?: 'System' | 'Entity' | 'Record';\n /** The target entity name (required for Entity/Record scope) */\n EntityName?: string;\n /** The record's primary key pairs (required for Record scope). Key = field name, Value = field value. */\n RecordKeys?: Array<{ Key: string; Value: string }>;\n /** Optional parent label ID for grouping */\n ParentID?: string;\n /** Optional external system reference */\n ExternalSystemID?: string;\n /** Whether to include dependent records when scope is Record (default: true) */\n IncludeDependencies?: boolean;\n /** Maximum depth of dependency graph traversal */\n MaxDepth?: number;\n /** Entity names to exclude from dependency traversal */\n ExcludeEntities?: string[];\n /**\n * Optional callback invoked with progress updates during label creation.\n * Requires an active PushStatusUpdates subscription (handled automatically).\n */\n OnProgress?: (progress: CreateVersionLabelProgress) => void;\n}\n\n/**\n * Result returned from the CreateVersionLabel mutation.\n */\nexport interface CreateVersionLabelResult {\n Success: boolean;\n LabelID?: string;\n LabelName?: string;\n ItemsCaptured?: number;\n SyntheticSnapshotsCreated?: number;\n Error?: string;\n CaptureErrors?: Array<{\n EntityName: string;\n RecordID: string;\n ErrorMessage: string;\n }>;\n}\n\n// =========================================================================\n// GraphQL Client\n// =========================================================================\n\n/**\n * Client for executing Version History operations through GraphQL.\n *\n * This class provides an easy way to create version labels with proper\n * server-side snapshot capture from a client application.\n *\n * @example\n * ```typescript\n * const vhClient = new GraphQLVersionHistoryClient(graphQLProvider);\n *\n * const result = await vhClient.CreateLabel({\n * Name: 'Before Refactor',\n * Scope: 'Record',\n * EntityName: 'MJ: AI Prompts',\n * RecordKeys: [{ Key: 'ID', Value: recordId }],\n * IncludeDependencies: true,\n * });\n *\n * if (result.Success) {\n * console.log(`Created label ${result.LabelID} with ${result.ItemsCaptured} items`);\n * }\n * ```\n */\nexport class GraphQLVersionHistoryClient {\n private _dataProvider: GraphQLDataProvider;\n\n constructor(dataProvider: GraphQLDataProvider) {\n this._dataProvider = dataProvider;\n }\n\n /**\n * Create a version label with full server-side snapshot capture.\n *\n * This invokes the VersionHistoryEngine on the server which:\n * 1. Creates the VersionLabel record\n * 2. Captures snapshots (VersionLabelItems) based on scope\n * 3. Updates the label with item count and duration metrics\n *\n * If `params.OnProgress` is provided, subscribes to PushStatusUpdates\n * for real-time progress during the operation.\n */\n public async CreateLabel(params: CreateVersionLabelParams): Promise<CreateVersionLabelResult> {\n let subscription: { unsubscribe: () => void } | undefined;\n\n try {\n // Subscribe to progress updates if callback provided\n if (params.OnProgress) {\n subscription = this._dataProvider.PushStatusUpdates(this._dataProvider.sessionId)\n .subscribe((message: string) => {\n try {\n const parsed = JSON.parse(message);\n if (parsed.resolver === 'VersionHistoryResolver' &&\n parsed.type === 'CreateLabelProgress' &&\n parsed.status === 'ok' &&\n parsed.data) {\n params.OnProgress!(parsed.data as CreateVersionLabelProgress);\n }\n } catch (_e) {\n // Ignore parse errors on progress messages\n }\n });\n }\n\n const mutation = gql`\n mutation CreateVersionLabel($input: CreateVersionLabelInput!, $sessionId: String) {\n CreateVersionLabel(input: $input, sessionId: $sessionId) {\n Success\n LabelID\n LabelName\n ItemsCaptured\n SyntheticSnapshotsCreated\n Error\n CaptureErrors {\n EntityName\n RecordID\n ErrorMessage\n }\n }\n }\n `;\n\n const variables = {\n input: this.buildInput(params),\n sessionId: this._dataProvider.sessionId,\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n return this.processResult(result);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n LogError(`GraphQLVersionHistoryClient.CreateLabel error: ${msg}`);\n return { Success: false, Error: msg };\n } finally {\n // Always clean up subscription\n if (subscription) {\n subscription.unsubscribe();\n }\n }\n }\n\n private buildInput(params: CreateVersionLabelParams): Record<string, unknown> {\n const input: Record<string, unknown> = {\n Name: params.Name,\n };\n\n if (params.Description != null) input.Description = params.Description;\n if (params.Scope != null) input.Scope = params.Scope;\n if (params.EntityName != null) input.EntityName = params.EntityName;\n if (params.ParentID != null) input.ParentID = params.ParentID;\n if (params.ExternalSystemID != null) input.ExternalSystemID = params.ExternalSystemID;\n if (params.IncludeDependencies != null) input.IncludeDependencies = params.IncludeDependencies;\n if (params.MaxDepth != null) input.MaxDepth = params.MaxDepth;\n if (params.ExcludeEntities != null) input.ExcludeEntities = params.ExcludeEntities;\n\n if (params.RecordKeys && params.RecordKeys.length > 0) {\n input.RecordKeys = params.RecordKeys.map(kv => ({\n Key: kv.Key,\n Value: kv.Value,\n }));\n }\n\n return input;\n }\n\n private processResult(result: Record<string, unknown>): CreateVersionLabelResult {\n const data = result?.CreateVersionLabel as Record<string, unknown> | undefined;\n if (!data) {\n return { Success: false, Error: 'Invalid response from server.' };\n }\n\n const captureErrors = Array.isArray(data.CaptureErrors)\n ? (data.CaptureErrors as Array<Record<string, string>>).map(e => ({\n EntityName: e.EntityName ?? '',\n RecordID: e.RecordID ?? '',\n ErrorMessage: e.ErrorMessage ?? '',\n }))\n : undefined;\n\n return {\n Success: data.Success as boolean,\n LabelID: data.LabelID as string | undefined,\n LabelName: data.LabelName as string | undefined,\n ItemsCaptured: data.ItemsCaptured as number | undefined,\n SyntheticSnapshotsCreated: data.SyntheticSnapshotsCreated as number | undefined,\n Error: data.Error as string | undefined,\n CaptureErrors: captureErrors,\n };\n }\n}\n","import { LogError } from \"@memberjunction/core\";\nimport { GraphQLDataProvider } from \"./graphQLDataProvider\";\nimport { gql } from \"graphql-request\";\n\n/**\n * Client for file storage operations through GraphQL.\n * This class provides an easy way to interact with file storage accounts from a client application.\n *\n * All operations use accountId (FileStorageAccount ID) as the primary identifier,\n * supporting the enterprise model where storage accounts are organizational resources.\n *\n * @example\n * ```typescript\n * // Create the client\n * const storageClient = new GraphQLFileStorageClient(graphQLProvider);\n *\n * // List objects in a directory\n * const objects = await storageClient.ListObjects(accountId, 'documents/');\n *\n * // Create a pre-authenticated upload URL\n * const uploadResult = await storageClient.CreatePreAuthUploadUrl(\n * accountId,\n * 'documents/report.pdf',\n * 'application/pdf'\n * );\n * ```\n */\nexport class GraphQLFileStorageClient {\n /**\n * The GraphQLDataProvider instance used to execute GraphQL requests\n * @private\n */\n private _dataProvider: GraphQLDataProvider;\n\n /**\n * Creates a new GraphQLFileStorageClient instance.\n * @param dataProvider The GraphQL data provider to use for queries\n */\n constructor(dataProvider: GraphQLDataProvider) {\n this._dataProvider = dataProvider;\n }\n\n // =========================================================================\n // Navigation / Directory Operations\n // =========================================================================\n\n /**\n * List objects in a storage account at the specified path.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param prefix The path prefix to list objects from (e.g., 'documents/')\n * @param delimiter Optional delimiter for grouping results (default: '/')\n * @returns A Promise that resolves to a StorageListResult\n *\n * @example\n * ```typescript\n * const result = await storageClient.ListObjects(accountId, 'documents/', '/');\n * console.log('Files:', result.objects.filter(o => !o.isDirectory));\n * console.log('Folders:', result.prefixes);\n * ```\n */\n public async ListObjects(\n accountId: string,\n prefix: string = '',\n delimiter?: string\n ): Promise<StorageListResult> {\n try {\n const query = gql`\n query ListStorageObjects($input: ListStorageObjectsInput!) {\n ListStorageObjects(input: $input) {\n objects {\n name\n path\n fullPath\n size\n contentType\n lastModified\n isDirectory\n etag\n cacheControl\n }\n prefixes\n }\n }\n `;\n\n const variables = {\n input: {\n AccountID: accountId,\n Prefix: prefix,\n Delimiter: delimiter || '/'\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(query, variables);\n\n if (!result?.ListStorageObjects) {\n throw new Error(\"Invalid response from server\");\n }\n\n return {\n objects: result.ListStorageObjects.objects.map((obj: StorageObjectMetadataResponse) => ({\n ...obj,\n lastModified: new Date(obj.lastModified)\n })),\n prefixes: result.ListStorageObjects.prefixes\n };\n } catch (e) {\n const error = e as Error;\n LogError(`Error listing storage objects: ${error}`);\n throw error;\n }\n }\n\n /**\n * Check if a directory exists in the storage account.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param path The directory path to check\n * @returns A Promise that resolves to true if the directory exists\n */\n public async DirectoryExists(\n accountId: string,\n path: string\n ): Promise<boolean> {\n try {\n // Check by listing with the path as prefix and looking for any results\n const result = await this.ListObjects(accountId, path.endsWith('/') ? path : `${path}/`, '/');\n return result.objects.length > 0 || result.prefixes.length > 0;\n } catch (e) {\n const error = e as Error;\n LogError(`Error checking directory existence: ${error}`);\n return false;\n }\n }\n\n /**\n * Create a directory in the storage account.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param path The directory path to create\n * @returns A Promise that resolves to true if the directory was created successfully\n */\n public async CreateDirectory(\n accountId: string,\n path: string\n ): Promise<boolean> {\n try {\n const mutation = gql`\n mutation CreateDirectory($input: CreateDirectoryInput!) {\n CreateDirectory(input: $input)\n }\n `;\n\n const variables = {\n input: {\n AccountID: accountId,\n Path: path\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n return result?.CreateDirectory ?? false;\n } catch (e) {\n const error = e as Error;\n LogError(`Error creating directory: ${error}`);\n return false;\n }\n }\n\n // =========================================================================\n // File Operations\n // =========================================================================\n\n /**\n * Check if an object exists in the storage account.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param objectName The name/path of the object to check\n * @returns A Promise that resolves to true if the object exists\n */\n public async ObjectExists(\n accountId: string,\n objectName: string\n ): Promise<boolean> {\n try {\n // Try to list the exact object\n const parentPath = objectName.substring(0, objectName.lastIndexOf('/') + 1);\n const fileName = objectName.substring(objectName.lastIndexOf('/') + 1);\n\n const result = await this.ListObjects(accountId, parentPath, '/');\n return result.objects.some(obj => obj.name === fileName || obj.fullPath === objectName);\n } catch (e) {\n const error = e as Error;\n LogError(`Error checking object existence: ${error}`);\n return false;\n }\n }\n\n /**\n * Create a pre-authenticated URL for uploading a file.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param objectName The name/path of the object to upload\n * @param contentType Optional content type for the file\n * @returns A Promise that resolves to the upload URL and provider key\n *\n * @example\n * ```typescript\n * const result = await storageClient.CreatePreAuthUploadUrl(\n * accountId,\n * 'documents/report.pdf',\n * 'application/pdf'\n * );\n *\n * // Use the upload URL to upload the file\n * await fetch(result.uploadUrl, {\n * method: 'PUT',\n * body: fileContent,\n * headers: { 'Content-Type': 'application/pdf' }\n * });\n * ```\n */\n public async CreatePreAuthUploadUrl(\n accountId: string,\n objectName: string,\n contentType?: string\n ): Promise<CreatePreAuthUploadUrlResult> {\n try {\n const mutation = gql`\n mutation CreatePreAuthUploadUrl($input: CreatePreAuthUploadUrlInput!) {\n CreatePreAuthUploadUrl(input: $input) {\n UploadUrl\n ProviderKey\n }\n }\n `;\n\n const variables = {\n input: {\n AccountID: accountId,\n ObjectName: objectName,\n ContentType: contentType\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n\n if (!result?.CreatePreAuthUploadUrl) {\n throw new Error(\"Invalid response from server\");\n }\n\n return {\n uploadUrl: result.CreatePreAuthUploadUrl.UploadUrl,\n providerKey: result.CreatePreAuthUploadUrl.ProviderKey\n };\n } catch (e) {\n const error = e as Error;\n LogError(`Error creating pre-auth upload URL: ${error}`);\n throw error;\n }\n }\n\n /**\n * Create a pre-authenticated URL for downloading a file.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param objectName The name/path of the object to download\n * @returns A Promise that resolves to the download URL\n *\n * @example\n * ```typescript\n * const downloadUrl = await storageClient.CreatePreAuthDownloadUrl(\n * accountId,\n * 'documents/report.pdf'\n * );\n *\n * // Use the download URL\n * window.open(downloadUrl, '_blank');\n * ```\n */\n public async CreatePreAuthDownloadUrl(\n accountId: string,\n objectName: string\n ): Promise<string> {\n try {\n const query = gql`\n query CreatePreAuthDownloadUrl($input: CreatePreAuthDownloadUrlInput!) {\n CreatePreAuthDownloadUrl(input: $input)\n }\n `;\n\n const variables = {\n input: {\n AccountID: accountId,\n ObjectName: objectName\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(query, variables);\n\n if (result?.CreatePreAuthDownloadUrl === undefined) {\n throw new Error(\"Invalid response from server\");\n }\n\n return result.CreatePreAuthDownloadUrl;\n } catch (e) {\n const error = e as Error;\n LogError(`Error creating pre-auth download URL: ${error}`);\n throw error;\n }\n }\n\n /**\n * Delete an object from the storage account.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param objectName The name/path of the object to delete\n * @returns A Promise that resolves to true if the object was deleted successfully\n */\n public async DeleteObject(\n accountId: string,\n objectName: string\n ): Promise<boolean> {\n try {\n const mutation = gql`\n mutation DeleteStorageObject($input: DeleteStorageObjectInput!) {\n DeleteStorageObject(input: $input)\n }\n `;\n\n const variables = {\n input: {\n AccountID: accountId,\n ObjectName: objectName\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n return result?.DeleteStorageObject ?? false;\n } catch (e) {\n const error = e as Error;\n LogError(`Error deleting storage object: ${error}`);\n return false;\n }\n }\n\n /**\n * Move/rename an object within the storage account.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param oldName The current name/path of the object\n * @param newName The new name/path for the object\n * @returns A Promise that resolves to true if the object was moved successfully\n */\n public async MoveObject(\n accountId: string,\n oldName: string,\n newName: string\n ): Promise<boolean> {\n try {\n const mutation = gql`\n mutation MoveStorageObject($input: MoveStorageObjectInput!) {\n MoveStorageObject(input: $input)\n }\n `;\n\n const variables = {\n input: {\n AccountID: accountId,\n OldName: oldName,\n NewName: newName\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n return result?.MoveStorageObject ?? false;\n } catch (e) {\n const error = e as Error;\n LogError(`Error moving storage object: ${error}`);\n return false;\n }\n }\n\n /**\n * Copy an object within the storage account.\n *\n * @param accountId The ID of the FileStorageAccount\n * @param sourceName The source name/path of the object\n * @param destinationName The destination name/path for the copy\n * @returns A Promise that resolves to true if the object was copied successfully\n */\n public async CopyObject(\n accountId: string,\n sourceName: string,\n destinationName: string\n ): Promise<boolean> {\n try {\n const mutation = gql`\n mutation CopyStorageObject($input: CopyStorageObjectInput!) {\n CopyStorageObject(input: $input)\n }\n `;\n\n const variables = {\n input: {\n AccountID: accountId,\n SourceName: sourceName,\n DestinationName: destinationName\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n return result?.CopyStorageObject ?? false;\n } catch (e) {\n const error = e as Error;\n LogError(`Error copying storage object: ${error}`);\n return false;\n }\n }\n\n /**\n * Copy an object between two different storage accounts.\n *\n * @param sourceAccountId The ID of the source FileStorageAccount\n * @param destinationAccountId The ID of the destination FileStorageAccount\n * @param sourcePath The source path of the object\n * @param destinationPath The destination path for the copy\n * @returns A Promise that resolves to the copy result\n */\n public async CopyObjectBetweenAccounts(\n sourceAccountId: string,\n destinationAccountId: string,\n sourcePath: string,\n destinationPath: string\n ): Promise<CopyBetweenAccountsResult> {\n try {\n const mutation = gql`\n mutation CopyObjectBetweenAccounts($input: CopyObjectBetweenAccountsInput!) {\n CopyObjectBetweenAccounts(input: $input) {\n success\n message\n bytesTransferred\n sourceAccount\n destinationAccount\n sourcePath\n destinationPath\n }\n }\n `;\n\n const variables = {\n input: {\n SourceAccountID: sourceAccountId,\n DestinationAccountID: destinationAccountId,\n SourcePath: sourcePath,\n DestinationPath: destinationPath\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(mutation, variables);\n\n if (!result?.CopyObjectBetweenAccounts) {\n throw new Error(\"Invalid response from server\");\n }\n\n return {\n success: result.CopyObjectBetweenAccounts.success,\n message: result.CopyObjectBetweenAccounts.message,\n bytesTransferred: result.CopyObjectBetweenAccounts.bytesTransferred,\n sourceAccount: result.CopyObjectBetweenAccounts.sourceAccount,\n destinationAccount: result.CopyObjectBetweenAccounts.destinationAccount,\n sourcePath: result.CopyObjectBetweenAccounts.sourcePath,\n destinationPath: result.CopyObjectBetweenAccounts.destinationPath\n };\n } catch (e) {\n const error = e as Error;\n LogError(`Error copying object between accounts: ${error}`);\n return {\n success: false,\n message: error.message,\n sourceAccount: '',\n destinationAccount: '',\n sourcePath,\n destinationPath\n };\n }\n }\n\n // =========================================================================\n // Search Operations\n // =========================================================================\n\n /**\n * Search for files across one or more storage accounts.\n *\n * @param accountIds Array of FileStorageAccount IDs to search\n * @param query The search query\n * @param options Optional search options\n * @returns A Promise that resolves to the search results\n *\n * @example\n * ```typescript\n * const results = await storageClient.SearchFiles(\n * [accountId1, accountId2],\n * 'quarterly report',\n * {\n * maxResultsPerAccount: 10,\n * fileTypes: ['pdf', 'docx'],\n * searchContent: true\n * }\n * );\n *\n * for (const accountResult of results.accountResults) {\n * console.log(`Results from ${accountResult.accountName}:`);\n * for (const file of accountResult.results) {\n * console.log(` - ${file.name} (${file.relevance})`);\n * }\n * }\n * ```\n */\n public async SearchFiles(\n accountIds: string[],\n searchQuery: string,\n options?: FileSearchOptions\n ): Promise<SearchAcrossAccountsResult> {\n try {\n const gqlQuery = gql`\n query SearchAcrossAccounts($input: SearchAcrossAccountsInput!) {\n SearchAcrossAccounts(input: $input) {\n accountResults {\n accountID\n accountName\n success\n errorMessage\n results {\n path\n name\n size\n contentType\n lastModified\n relevance\n excerpt\n matchInFilename\n objectId\n }\n totalMatches\n hasMore\n nextPageToken\n }\n totalResultsReturned\n successfulAccounts\n failedAccounts\n }\n }\n `;\n\n const variables = {\n input: {\n AccountIDs: accountIds,\n Query: searchQuery,\n MaxResultsPerAccount: options?.maxResultsPerAccount,\n FileTypes: options?.fileTypes,\n SearchContent: options?.searchContent\n }\n };\n\n const result = await this._dataProvider.ExecuteGQL(gqlQuery, variables);\n\n if (!result?.SearchAcrossAccounts) {\n throw new Error(\"Invalid response from server\");\n }\n\n const searchResult = result.SearchAcrossAccounts;\n\n return {\n accountResults: searchResult.accountResults.map((ar: AccountSearchResultResponse) => ({\n accountId: ar.accountID,\n accountName: ar.accountName,\n success: ar.success,\n errorMessage: ar.errorMessage,\n results: ar.results.map((r: FileSearchResultResponse) => ({\n path: r.path,\n name: r.name,\n size: r.size,\n contentType: r.contentType,\n lastModified: new Date(r.lastModified),\n relevance: r.relevance,\n excerpt: r.excerpt,\n matchInFilename: r.matchInFilename,\n objectId: r.objectId\n })),\n totalMatches: ar.totalMatches,\n hasMore: ar.hasMore,\n nextPageToken: ar.nextPageToken\n })),\n totalResultsReturned: searchResult.totalResultsReturned,\n successfulAccounts: searchResult.successfulAccounts,\n failedAccounts: searchResult.failedAccounts\n };\n } catch (e) {\n const error = e as Error;\n LogError(`Error searching across accounts: ${error}`);\n return {\n accountResults: [],\n totalResultsReturned: 0,\n successfulAccounts: 0,\n failedAccounts: accountIds.length\n };\n }\n }\n}\n\n// =========================================================================\n// Type Definitions\n// =========================================================================\n\n/**\n * Metadata for a storage object\n */\nexport interface StorageObjectMetadata {\n /** The name of the object (filename) */\n name: string;\n /** The path to the object (directory) */\n path: string;\n /** The full path including name */\n fullPath: string;\n /** Size in bytes */\n size: number;\n /** MIME content type */\n contentType: string;\n /** Last modification date */\n lastModified: Date;\n /** Whether this is a directory */\n isDirectory: boolean;\n /** ETag for caching */\n etag?: string;\n /** Cache control header */\n cacheControl?: string;\n}\n\n/**\n * Response type for storage object metadata (with string date)\n */\ninterface StorageObjectMetadataResponse {\n name: string;\n path: string;\n fullPath: string;\n size: number;\n contentType: string;\n lastModified: string;\n isDirectory: boolean;\n etag?: string;\n cacheControl?: string;\n}\n\n/**\n * Result from listing storage objects\n */\nexport interface StorageListResult {\n /** Array of objects in the directory */\n objects: StorageObjectMetadata[];\n /** Array of subdirectory prefixes */\n prefixes: string[];\n}\n\n/**\n * Result from creating a pre-authenticated upload URL\n */\nexport interface CreatePreAuthUploadUrlResult {\n /** The URL to use for uploading */\n uploadUrl: string;\n /** The provider-specific key for the object */\n providerKey?: string;\n}\n\n/**\n * Result from copying an object between accounts\n */\nexport interface CopyBetweenAccountsResult {\n /** Whether the copy was successful */\n success: boolean;\n /** Human-readable message */\n message: string;\n /** Number of bytes transferred */\n bytesTransferred?: number;\n /** Name of the source account */\n sourceAccount: string;\n /** Name of the destination account */\n destinationAccount: string;\n /** Source path */\n sourcePath: string;\n /** Destination path */\n destinationPath: string;\n}\n\n/**\n * Options for file search operations\n */\nexport interface FileSearchOptions {\n /** Maximum results per account */\n maxResultsPerAccount?: number;\n /** Filter by file types (extensions) */\n fileTypes?: string[];\n /** Whether to search file content (not just names) */\n searchContent?: boolean;\n}\n\n/**\n * A single file search result\n */\nexport interface FileSearchResult {\n /** Path to the file */\n path: string;\n /** File name */\n name: string;\n /** File size in bytes */\n size: number;\n /** MIME content type */\n contentType: string;\n /** Last modification date */\n lastModified: Date;\n /** Relevance score (0-1) */\n relevance?: number;\n /** Text excerpt showing the match */\n excerpt?: string;\n /** Whether the match was in the filename */\n matchInFilename?: boolean;\n /** Provider-specific object ID */\n objectId?: string;\n}\n\n/**\n * Response type for file search result (with string date)\n */\ninterface FileSearchResultResponse {\n path: string;\n name: string;\n size: number;\n contentType: string;\n lastModified: string;\n relevance?: number;\n excerpt?: string;\n matchInFilename?: boolean;\n objectId?: string;\n}\n\n/**\n * Search results for a single account\n */\nexport interface AccountSearchResult {\n /** The account ID */\n accountId: string;\n /** The account name */\n accountName: string;\n /** Whether the search was successful */\n success: boolean;\n /** Error message if search failed */\n errorMessage?: string;\n /** Search results */\n results: FileSearchResult[];\n /** Total matches found */\n totalMatches?: number;\n /** Whether there are more results */\n hasMore: boolean;\n /** Token for pagination */\n nextPageToken?: string;\n}\n\n/**\n * Response type for account search result (with string dates in results)\n */\ninterface AccountSearchResultResponse {\n accountID: string;\n accountName: string;\n success: boolean;\n errorMessage?: string;\n results: FileSearchResultResponse[];\n totalMatches?: number;\n hasMore: boolean;\n nextPageToken?: string;\n}\n\n/**\n * Result from searching across multiple accounts\n */\nexport interface SearchAcrossAccountsResult {\n /** Results grouped by account */\n accountResults: AccountSearchResult[];\n /** Total results returned across all accounts */\n totalResultsReturned: number;\n /** Number of accounts that were searched successfully */\n successfulAccounts: number;\n /** Number of accounts that failed to search */\n failedAccounts: number;\n}\n"],"names":["FieldMapper","__name","obj","k","fieldName","v","reversed","GraphQLTransactionGroup","TransactionGroupBase","provider","mutation","gql","items","pt","vars","results","data","returnResults","resultJSON","resultObject","SafeJSONParse","item","TransactionResult","GraphQLAIClient","dataProvider","params","variables","result","e","promptResult","parsedResult","validationResult","chatResult","error","LogError","sourceArtifactId","sourceArtifactVersionId","subscription","message","LogStatusEx","parsed","progressWithRunId","errorMessage","isFetchError","isTimeoutError","userMessage","completionTimeoutId","resolveCompletion","rejectCompletion","completionPromise","resolve","reject","ack","embedResult","DEFAULT_CATEGORY","BrowserStorageProviderBase","category","cat","categoryMap","key","value","IDB_DB_NAME","IDB_DB_VERSION","KNOWN_OBJECT_STORES","LEGACY_STORE_NAME","BrowserIndexedDBStorageProvider","openDB","db","storeName","LogErrorEx","storeKey","tx","prefix","store","allKeys","GraphQLProviderConfigData","ProviderConfigDataBase","token","url","wsurl","refreshTokenFunction","MJCoreSchemaName","includeSchemas","excludeSchemas","mjAPIKey","userAPIKey","GraphQLDataProvider","ProviderBase","uuidv4","ls","sessionId","forceRefreshSessionId","oldUUID","configData","providerToUse","separateConnection","d","u","roles","r","UserInfo","contextUser","query","sql","maxRows","timeoutSeconds","input","p","QueryID","CategoryID","CategoryPath","Parameters","MaxRows","StartRow","QueryName","response","transformedResults","deserializedResults","qName","paramType","innerParams","entity","viewEntity","entityName","dynamicView","graphQLTypeName","getGraphQLTypeNameBase","a","fieldList","aggregateResponseFields","viewData","responseAggregates","codeNameDiffFields","f","entityInfos","param","innerParam","index","deserializeData","inputItem","deserializedUpdatedRows","ViewInfo","mapper","kv","c","ProviderType","primaryKey","b","kvps","request","recordID","options","newRequest","fm","user","BaseEntityResult","type","mutationName","filteredFields","inner","outer","i","val","EntityFieldTSType","numValue","ov","mutationInputTypes","TransactionItem","success","EntityRelationshipsToLoad","pkeyInnerParamString","pkeyOuterParamString","field","pkeyGraphQLType","rel","ret","entityInfo","re","uniqueCodeName","returnValues","pk","queryName","returnedVal","originalVal","datasetName","itemFilters","userId","isFavorite","info","CompositeKey","dataContextID","dataContextItemID","refreshTokenIfNeeded","isInstanceSingleton","newToken","newClient","headers","GraphQLClient","UserRoleInfo","object","sOutput","keys","InMemoryLocalStorageProvider","now","createClient","entry","initialCount","entries","toRemove","timeSinceRequested","timeSinceEmission","Observable","observer","client","unsubscribe","errorObj","refreshError","existing","SUBSCRIBE_TO_STATUS","subject","Subject","Subscription","sub","recordPKValue","name","setupGraphQLClient","config","SetProvider","StartupManager","MJGlobal","MJEventType","SyncRolesAndUsersResult","RoleInput","UserInput","RolesAndUsersInput","SyncDataAction","ActionItemInput","SyncDataResult","ActionItemOutput","GraphQLSystemUserClient","queries","accessToken","cleanError","match","IsVerboseLoggingEnabled","verboseError","ID","errorDetails","cause","agentResult","GetDataOutput","SimpleRemoteEntityOutput","SimpleRemoteEntity","SimpleRemoteEntityField","GraphQLActionClient","actionID","skipActionLog","serializedParams","originalParams","resultData","entityObject","kvp","GraphQLEncryptionClient","apiKeyId","GraphQLTestingClient","tagsJson","variablesJson","selectedTestIdsJson","testId","operation","errorMsg","GraphQLComponentRegistryClient","spec","searchResult","json","registryId","componentId","registryName","namespace","GraphQLVersionHistoryClient","msg","captureErrors","GraphQLFileStorageClient","accountId","delimiter","path","objectName","parentPath","fileName","contentType","oldName","newName","sourceName","destinationName","sourceAccountId","destinationAccountId","sourcePath","destinationPath","accountIds","searchQuery","gqlQuery","ar"],"mappings":"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"}