@visionfi/server-sdk
Version:
Server-side SDK for VisionFI API access using Google Service Account authentication
1,103 lines (1,102 loc) • 42.5 kB
JavaScript
import axios from 'axios';
import { GoogleAuth } from 'google-auth-library';
import * as fs from 'fs';
import * as path from 'path';
import { VisionFiError } from './types/index.js';
import { DocuSignIntegration } from './integrations/docusign/index.js';
/**
* Main client for interacting with the VisionFi platform API.
* Handles authentication, document analysis, and results retrieval.
*
* @example
* ```typescript
* // With explicit service account credentials
* const client = new VisionFi({
* serviceAccountPath: './service-account.json'
* });
*
* // Using Application Default Credentials (ADC) in GCP environments
* const client = new VisionFi(); // Automatically uses ADC
*
* // Verify authentication
* const authResult = await client.verifyAuth();
*
* // Analyze a document
* const job = await client.analyzeDocument(fileBuffer, {
* fileName: 'document.pdf',
* analysisType: 'auto_loan_abstract'
* });
*
* // Get results
* const results = await client.getResults(job.uuid);
* ```
*/
export class VisionFi {
/** Base URL for the VisionFi API */
apiBaseUrl;
/** Google Auth instance for authentication */
auth;
/** Axios instance for making HTTP requests */
apiClient;
/** DocuSign integration instance */
docuSignIntegration;
/**
* Utility method to extract Authorization header from various header object types
* @param headers - Headers object (could be Map, Headers, or plain object)
* @returns Authorization header value or null if not found
*/
getAuthorizationHeader(headers) {
// Handle null/undefined
if (!headers) {
return null;
}
// Handle Headers object or Map with .get() method
if (typeof headers.get === 'function') {
return headers.get('Authorization');
}
// Handle plain JavaScript object
if (typeof headers === 'object') {
return headers['Authorization'] || headers.authorization || null;
}
return null;
}
constructor(config = {}) {
this.apiBaseUrl = config.apiBaseUrl || 'https://platform.visionfi.ai/api/v1';
if (config.serviceAccountJson) {
this.auth = new GoogleAuth({
credentials: config.serviceAccountJson,
// No scopes needed for ID tokens
});
}
else if (config.serviceAccountPath) {
const serviceAccountPath = path.resolve(config.serviceAccountPath);
try {
const serviceAccountJson = JSON.parse(fs.readFileSync(serviceAccountPath, 'utf8'));
this.auth = new GoogleAuth({
credentials: serviceAccountJson,
// No scopes needed for ID tokens
});
}
catch (error) {
throw new VisionFiError(`Unable to read service account file: ${error.message}`);
}
}
else {
// Use Application Default Credentials (ADC) when no explicit credentials are provided
// This enables the SDK to work seamlessly in GCP environments (Cloud Run, GKE, App Engine, etc.)
// where service account credentials are automatically provided by the environment
this.auth = new GoogleAuth({
// ADC will automatically find credentials in this order:
// 1. GOOGLE_APPLICATION_CREDENTIALS environment variable
// 2. gcloud credentials (for local development)
// 3. Metadata server (for GCP services)
});
}
this.apiClient = axios.create({
baseURL: this.apiBaseUrl,
headers: {
'Accept': 'application/json'
}
});
// Add authentication to all requests
this.apiClient.interceptors.request.use(async (config) => {
const client = await this.auth.getIdTokenClient("platform.visionfi.ai");
const headers = await client.getRequestHeaders();
config.headers = config.headers || {};
const authHeader = this.getAuthorizationHeader(headers);
if (authHeader) {
config.headers['Authorization'] = authHeader;
}
return config;
});
// Add global error handling
this.apiClient.interceptors.response.use((response) => response, (error) => {
const { response } = error;
let message = 'An unknown error occurred';
let code = 'unknown_error';
let statusCode = 500;
if (response) {
statusCode = response.status;
message = response.data?.message || `HTTP Error: ${response.status} ${response.statusText}`;
code = response.data?.code || `http_${response.status}`;
}
else if (error.request) {
message = 'No response received from server';
code = 'network_error';
}
else {
message = error.message;
code = 'request_setup_error';
}
throw new VisionFiError(message, statusCode, code);
});
// Initialize DocuSign integration
this.docuSignIntegration = new DocuSignIntegration(this.apiClient);
}
/**
* Verifies authentication with the VisionFi API
*/
async verifyAuth() {
try {
const response = await this.apiClient.post('/auth/verify');
return response.data;
}
catch (error) {
throw this.handleError(error, 'Authentication verification failed');
}
}
/**
* Retrieves the JWT authentication token to use with the VisionFi API
* @returns A promise that resolves to an object containing the token
* @example
* ```typescript
* const { token } = await client.getAuthToken();
* console.log('JWT Token:', token);
*
* // Can be used in other API clients or tools like Postman
* // Example: Authorization: Bearer [token]
* ```
*/
async getAuthToken() {
try {
const client = await this.auth.getIdTokenClient("platform.visionfi.ai");
const headers = await client.getRequestHeaders();
const authHeader = this.getAuthorizationHeader(headers);
if (!authHeader) {
throw new VisionFiError('No Authorization header found in authentication response');
}
// Extract token from 'Bearer TOKEN' format
const token = authHeader.replace('Bearer ', '');
return { token };
}
catch (error) {
throw this.handleError(error, 'Failed to generate authentication token');
}
}
/**
* Retrieves client information from the VisionFi API
* @returns A promise that resolves to client information and configuration
* @example
* ```typescript
* const clientInfo = await client.getClientInfo();
*
* if (clientInfo.success) {
* console.log('Client name:', clientInfo.data.name);
*
* // Check for specific features
* if (clientInfo.data.features?.includes('document-analysis')) {
* // Enable document analysis features
* }
* } else {
* console.error('Failed to get client info:', clientInfo.message);
* }
* ```
*/
async getClientInfo() {
try {
const response = await this.apiClient.get('/operations/getClientInfo');
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to retrieve client information');
}
}
/**
* Retrieves available document analysis workflows from the VisionFi API
* @returns A promise that resolves to the list of available workflows
* @example
* ```typescript
* const workflowsResponse = await client.getWorkflows();
*
* if (workflowsResponse.success) {
* console.log('Available workflows:');
* workflowsResponse.data?.forEach(workflow => {
* console.log(`- ${workflow.workflow_key}: ${workflow.description}`);
* });
* } else {
* console.error('Failed to get workflows:', workflowsResponse.message);
* }
* ```
*/
async getWorkflows() {
try {
const response = await this.apiClient.get('/operations/getAvailableWorkflows');
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to retrieve available workflows');
}
}
/**
* Uploads and analyzes a document
* @param fileBuffer The file buffer to upload
* @param options Options for document analysis
*/
async analyzeDocument(fileBuffer, options) {
try {
// Convert file buffer to Base64
const fileBase64 = fileBuffer.toString('base64');
const requestData = {
fileName: options.fileName,
fileBase64: fileBase64,
analysisType: options.analysisType
};
const response = await this.apiClient.post('/operations/analyze', requestData);
return response.data;
}
catch (error) {
throw this.handleError(error, 'Document analysis request failed');
}
}
/**
* Retrieves the results of an analysis job
* @param jobUuid The UUID of the analysis job
* @param pollInterval Optional interval in ms to poll for results until completed
* @param maxAttempts Optional maximum number of polling attempts
* @param earlyCancelAttempts Optional number of attempts before emitting a "job likely invalid" warning
*/
async getResults(jobUuid, pollInterval, maxAttempts, earlyCancelAttempts) {
if (!pollInterval) {
// Just fetch once without polling
try {
const response = await this.apiClient.get(`/operations/getAnalysisResults?uuid=${jobUuid}`);
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to retrieve analysis results');
}
}
// Poll until completion
const interval = pollInterval || 3000; // Default 3 seconds
const attempts = maxAttempts || 20; // Default 20 attempts (roughly 1 minute)
const earlyWarningAttempts = earlyCancelAttempts || Math.floor(attempts / 4); // Default 25% of max attempts
return new Promise((resolve, reject) => {
let attemptCount = 0;
let earlyWarningEmitted = false;
const poll = async () => {
attemptCount++;
try {
const response = await this.apiClient.get(`/operations/getAnalysisResults?uuid=${jobUuid}`);
const result = response.data;
// Check if results are found
if (result.found === false) {
// Results not found yet - continue polling
if (attemptCount >= earlyWarningAttempts && !earlyWarningEmitted) {
// After several attempts with no results, emit a warning through console
// This helps identify potentially invalid UUIDs earlier
console.warn(`Warning: No results found after ${attemptCount} attempts for UUID ${jobUuid}. This may indicate an invalid UUID or a long-running job.`);
earlyWarningEmitted = true;
}
if (attemptCount >= attempts) {
reject(new VisionFiError(`Maximum polling attempts (${attempts}) reached. The job may still be processing.`, 408, 'polling_timeout'));
return;
}
setTimeout(poll, interval);
return;
}
// Results found! Either processed or error
resolve(result);
}
catch (error) {
reject(this.handleError(error, 'Error while polling for results'));
}
};
poll();
});
}
handleError(error, defaultMessage) {
if (error instanceof VisionFiError) {
return error;
}
const message = error.message || defaultMessage;
const statusCode = error.response?.status;
const code = error.response?.data?.code || 'unknown_error';
return new VisionFiError(message, statusCode, code);
}
// DocuSign Configuration Management Methods
/**
* Retrieves the current DocuSign integration configuration.
*
* @returns A promise that resolves to the DocuSign configuration response
*
* @example
* ```typescript
* const configResponse = await client.getDocuSignConfig();
*
* if (configResponse.success) {
* console.log('DocuSign configuration:', configResponse.data);
* } else {
* console.error('Failed to get DocuSign config:', configResponse.message);
* }
* ```
*/
async getDocuSignConfig() {
return this.docuSignIntegration.config.getConfig();
}
/**
* Updates or creates the DocuSign integration configuration.
*
* @param config - The DocuSign configuration to update or create
* @returns A promise that resolves to the DocuSign configuration response
*
* @example
* ```typescript
* const configResponse = await client.updateDocuSignConfig({
* userId: '12345678-1234-1234-1234-123456789012',
* accountId: '98765432-9876-9876-9876-987654321098',
* baseUri: 'https://demo.docusign.net/restapi',
* name: 'Production DocuSign Integration',
* enabled: true
* });
*
* if (configResponse.success) {
* console.log('DocuSign configuration updated:', configResponse.data);
* } else {
* console.error('Failed to update DocuSign config:', configResponse.message);
* }
* ```
*/
async updateDocuSignConfig(config) {
return this.docuSignIntegration.config.updateConfig(config);
}
/**
* Updates only the authentication status of a DocuSign integration.
*
* @param statusUpdate - The authentication status update data
* @returns A promise that resolves to the DocuSign authentication status response
*
* @example
* ```typescript
* const statusResponse = await client.updateDocuSignAuthStatus({
* authenticationStatus: 'authenticated',
* lastAuthenticated: new Date().toISOString()
* });
*
* if (statusResponse.success) {
* console.log('DocuSign auth status updated:', statusResponse.data);
* } else {
* console.error('Failed to update auth status:', statusResponse.message);
* }
* ```
*/
async updateDocuSignAuthStatus(statusUpdate) {
return this.docuSignIntegration.config.updateAuthStatus(statusUpdate);
}
// DocuSign Connect Webhook Management Methods
/**
* Retrieves all webhook configurations for the client's DocuSign integration.
*
* @returns A promise that resolves to the list of webhook configurations
*
* @example
* ```typescript
* const webhooksResponse = await client.getDocuSignConnectWebhooks();
*
* if (webhooksResponse.success) {
* console.log('DocuSign webhooks:', webhooksResponse.data);
* } else {
* console.error('Failed to get webhooks:', webhooksResponse.message);
* }
* ```
*/
async getDocuSignConnectWebhooks() {
return this.docuSignIntegration.connect.getWebhooks();
}
/**
* Retrieves a specific webhook configuration by its GUID.
*
* @param webhookGuid - The unique identifier for the webhook configuration
* @returns A promise that resolves to the specific webhook configuration
*
* @example
* ```typescript
* const webhookGuid = 'd0381ee4-5b83-4d6b-9f37-177c2a6a79f2';
* const webhookResponse = await client.getDocuSignConnectWebhook(webhookGuid);
*
* if (webhookResponse.success) {
* console.log('DocuSign webhook:', webhookResponse.data);
* } else {
* console.error('Failed to get webhook:', webhookResponse.message);
* }
* ```
*/
async getDocuSignConnectWebhook(webhookGuid) {
return this.docuSignIntegration.connect.getWebhook(webhookGuid);
}
/**
* Creates a new DocuSign Connect webhook configuration.
*
* @param webhook - The webhook configuration to create
* @returns A promise that resolves to the created webhook configuration
*
* @example
* ```typescript
* const newWebhook = {
* webhookGuid: 'd0381ee4-5b83-4d6b-9f37-177c2a6a79f2',
* hmacSecret: '89c3444a-f4c2-4fda-98b8-59dc2fe3ea29',
* workflowId: 'electronic-signatures-workflow'
* };
*
* const webhookResponse = await client.addDocuSignConnectWebhook(newWebhook);
*
* if (webhookResponse.success) {
* console.log('DocuSign webhook created:', webhookResponse.data);
* } else {
* console.error('Failed to create webhook:', webhookResponse.message);
* }
* ```
*/
async addDocuSignConnectWebhook(webhook) {
return this.docuSignIntegration.connect.addWebhook(webhook);
}
/**
* Updates an existing DocuSign Connect webhook configuration.
*
* @param webhookGuid - The unique identifier for the webhook configuration
* @param webhook - The partial webhook configuration to update
* @returns A promise that resolves to the updated webhook configuration
*
* @example
* ```typescript
* const webhookGuid = 'd0381ee4-5b83-4d6b-9f37-177c2a6a79f2';
* const webhookUpdate = {
* hmacSecret: 'updated-hmac-secret-value',
* workflowId: 'updated-workflow-id'
* };
*
* const webhookResponse = await client.updateDocuSignConnectWebhook(webhookGuid, webhookUpdate);
*
* if (webhookResponse.success) {
* console.log('DocuSign webhook updated:', webhookResponse.data);
* } else {
* console.error('Failed to update webhook:', webhookResponse.message);
* }
* ```
*/
async updateDocuSignConnectWebhook(webhookGuid, webhook) {
return this.docuSignIntegration.connect.updateWebhook(webhookGuid, webhook);
}
/**
* Deletes a DocuSign Connect webhook configuration.
*
* @param webhookGuid - The unique identifier for the webhook configuration
* @returns A promise that resolves to the deletion response
*
* @example
* ```typescript
* const webhookGuid = 'd0381ee4-5b83-4d6b-9f37-177c2a6a79f2';
* const deleteResponse = await client.deleteDocuSignConnectWebhook(webhookGuid);
*
* if (deleteResponse.success) {
* console.log('DocuSign webhook deleted:', deleteResponse.message);
* } else {
* console.error('Failed to delete webhook:', deleteResponse.message);
* }
* ```
*/
async deleteDocuSignConnectWebhook(webhookGuid) {
return this.docuSignIntegration.connect.deleteWebhook(webhookGuid);
}
/**
* Gets a DocuSign OAuth consent URL for tenant authorization
* @param environment Which DocuSign environment to use ('prod' or 'dev')
* @returns A promise that resolves to the consent URL
*
* @example
* ```typescript
* // Generate a consent URL for the production DocuSign environment
* try {
* const consentUrl = await client.getDocuSignConsentUrl('prod');
* console.log('DocuSign consent URL:', consentUrl);
*
* // Provide this URL to the user to authorize DocuSign integration
* } catch (error) {
* console.error('Error generating consent URL:', error);
* }
* ```
*/
async getDocuSignConsentUrl(environment) {
if (environment !== 'prod' && environment !== 'dev') {
throw new Error('Invalid environment parameter. Must be either "prod" or "dev"');
}
try {
const response = await this.apiClient.get(`/admin/docusign/consent-url?environment=${environment}`);
if (response.data.success && response.data.data && response.data.data.consentUrl) {
return response.data.data.consentUrl;
}
else {
throw new VisionFiError(response.data.message || 'Failed to generate DocuSign consent URL');
}
}
catch (error) {
throw this.handleError(error, 'Failed to generate DocuSign consent URL');
}
}
/**
* Builds the complete webhook URL for a specific DocuSign Connect webhook
*
* @param webhookGuid - The unique identifier for the webhook configuration
* @returns A promise that resolves to the complete webhook URL that can be used in DocuSign Connect configuration
*
* @example
* ```typescript
* // Get the complete webhook URL for a specific webhook
* try {
* const webhookGuid = 'd0381ee4-5b83-4d6b-9f37-177c2a6a79f2';
* const webhookUrl = await client.getDocuSignWebhookUrl(webhookGuid);
*
* console.log('DocuSign Connect webhook URL:', webhookUrl);
* // Example: https://platform.visionfi.ai/api/v1/webhooks/tenant123/docusign/d0381ee4-5b83-4d6b-9f37-177c2a6a79f2
*
* // Use this URL in your DocuSign Connect configuration
* } catch (error) {
* console.error('Error generating webhook URL:', error.message);
* }
* ```
*/
async getDocuSignWebhookUrl(webhookGuid) {
if (!webhookGuid) {
throw new Error('Webhook GUID is required');
}
try {
// First, verify the webhook exists
await this.getDocuSignConnectWebhook(webhookGuid);
// Get the client info to retrieve the tenant key
const clientInfoResponse = await this.getClientInfo();
if (!clientInfoResponse.success || !clientInfoResponse.data?.tenantKey) {
throw new VisionFiError('Failed to retrieve tenant key from client information');
}
const tenantKey = clientInfoResponse.data.tenantKey;
// Construct the webhook URL
// Use the apiBaseUrl without the /api/v1 part since it will be included in the path
const baseUrl = this.apiBaseUrl;
// Construct the complete webhook URL
return `${baseUrl}/webhooks/${tenantKey}/docusign/${webhookGuid}`;
}
catch (error) {
throw this.handleError(error, 'Failed to generate DocuSign webhook URL');
}
}
// Package Management Methods
/**
* Creates a new package for document processing.
*
* @param options - The package creation options
* @returns A promise that resolves to the created package response
*
* @example
* ```typescript
* const packageResponse = await client.createPackage({
* description: 'Test vehicle loan application package',
* productType: 'consumer_loan_vehicle_installment',
* externalReferenceIds: ['APP-2024-001', 'LOCAL-ID-12345']
* });
*
* console.log('Package created:', packageResponse.packageId);
* ```
*/
async createPackage(options) {
try {
const response = await this.apiClient.post('/operations/package', options);
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to create package');
}
}
/**
* Retrieves a list of packages with optional filtering.
*
* @param options - Optional filtering options
* @returns A promise that resolves to the list of packages
*
* @example
* ```typescript
* // List all packages
* const allPackages = await client.listPackages();
*
* // List packages with filters
* const filteredPackages = await client.listPackages({
* externalReferences: 'APP-2024-001,LOCAL-ID-12345',
* productTypes: 'consumer_loan_vehicle_installment',
* createdAfter: '2024-01-15T10:30:00Z'
* });
*
* if (filteredPackages.success) {
* console.log('Found packages:', filteredPackages.data?.length);
* }
* ```
*/
async listPackages(options) {
try {
const params = new URLSearchParams();
// Add new concise filtering parameters
if (options?.tags)
params.append('tags', options.tags);
if (options?.ref1)
params.append('ref1', options.ref1);
if (options?.ref2)
params.append('ref2', options.ref2);
if (options?.ref3)
params.append('ref3', options.ref3);
if (options?.ref4)
params.append('ref4', options.ref4);
if (options?.ref5)
params.append('ref5', options.ref5);
if (options?.category)
params.append('category', options.category);
if (options?.owner)
params.append('owner', options.owner);
// Support legacy parameter for backward compatibility
if (options?.externalReferences)
params.append('externalReferences', options.externalReferences);
// Existing parameters
if (options?.productTypes)
params.append('productTypes', options.productTypes);
if (options?.createdAfter)
params.append('createdAfter', options.createdAfter);
const queryString = params.toString();
const url = queryString ? `/operations/packages?${queryString}` : '/operations/packages';
const response = await this.apiClient.get(url);
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to list packages');
}
}
/**
* Retrieves a specific package by its ID.
*
* @param packageId - The unique identifier for the package
* @returns A promise that resolves to the package information
*
* @example
* ```typescript
* const packageInfo = await client.getPackage('package-uuid-here');
*
* console.log('Package status:', packageInfo.status);
* console.log('Total files:', packageInfo.totalFiles);
* console.log('External references:', packageInfo.externalReferenceIds);
* ```
*/
async getPackage(packageId) {
try {
const response = await this.apiClient.get(`/operations/packages/${packageId}`);
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to get package');
}
}
/**
* Gets audit history for a specific package.
*
* @param packageId - The unique identifier for the package
* @param limit - Optional number of audit entries to return (default: 50, max: 200)
* @returns A promise that resolves to the package audit history
*
* @example
* ```typescript
* const auditHistory = await client.getPackageAuditHistory('package-uuid-here');
*
* console.log('Package audit history:', auditHistory.auditHistory);
* console.log('Total entries:', auditHistory.total);
*
* // With custom limit
* const limitedHistory = await client.getPackageAuditHistory('package-uuid-here', 100);
* ```
*/
async getPackageAuditHistory(packageId, limit) {
try {
const params = limit ? { limit } : {};
const response = await this.apiClient.get(`/operations/packages/${packageId}/audit`, { params });
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to get package audit history');
}
}
// Document Management Methods
/**
* Adds one or more documents to an existing package.
*
* @param packageId - The unique identifier for the package
* @param options - The documents to add
* @returns A promise that resolves to the add documents response
*
* @example
* ```typescript
* // Add single document
* const fileBuffer = fs.readFileSync('./document.pdf');
* const addResponse = await client.addDocumentsToPackage('package-uuid', {
* files: [{
* fileName: 'drivers_license.pdf',
* fileBase64: fileBuffer.toString('base64')
* }]
* });
*
* console.log('Files added:', addResponse.filesAdded);
* console.log('Total files:', addResponse.totalFiles);
* ```
*/
async addDocumentsToPackage(packageId, options) {
try {
const response = await this.apiClient.put(`/operations/packages/${packageId}/document`, options);
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to add documents to package');
}
}
/**
* Convenience method to add a single file buffer to a package.
*
* @param packageId - The unique identifier for the package
* @param fileBuffer - The file buffer to add
* @param fileName - The name of the file
* @returns A promise that resolves to the add documents response
*
* @example
* ```typescript
* const fileBuffer = fs.readFileSync('./document.pdf');
* const addResponse = await client.addFileToPackage(
* 'package-uuid',
* fileBuffer,
* 'document.pdf'
* );
*
* console.log('Files added:', addResponse.filesAdded);
* console.log('Total files:', addResponse.totalFiles);
* ```
*/
async addFileToPackage(packageId, fileBuffer, fileName) {
const fileBase64 = fileBuffer.toString('base64');
return this.addDocumentsToPackage(packageId, {
files: [{ fileName, fileBase64 }]
});
}
/**
* Soft deletes a document from a package.
*
* @param packageId - The unique identifier for the package
* @param documentUuid - The unique identifier for the document
* @returns A promise that resolves to the delete response
*
* @example
* ```typescript
* const deleteResponse = await client.deleteDocumentFromPackage(
* 'package-uuid',
* 'document-uuid'
* );
*
* if (deleteResponse.success) {
* console.log('Document deleted:', deleteResponse.message);
* }
* ```
*/
async deleteDocumentFromPackage(packageId, documentUuid) {
try {
const response = await this.apiClient.delete(`/operations/packages/${packageId}/document/${documentUuid}`);
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to delete document from package');
}
}
// External References Management Methods
/**
* Adds external reference IDs to a package.
*
* @param packageId - The unique identifier for the package
* @param options - The reference IDs to add
* @returns A promise that resolves to the add references response
*
* @example
* ```typescript
* const addRefsResponse = await client.addExternalReferences('package-uuid', {
* referenceIds: ['NEW-REF-001', 'UPDATED-ID-456']
* });
*
* if (addRefsResponse.success) {
* console.log('References added:', addRefsResponse.message);
* }
* ```
*/
async addExternalReferences(packageId, options) {
try {
const response = await this.apiClient.post(`/operations/packages/${packageId}/references`, options);
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to add external references');
}
}
/**
* Removes external reference IDs from a package.
*
* @param packageId - The unique identifier for the package
* @param options - The reference IDs to remove
* @returns A promise that resolves to the remove references response
*
* @example
* ```typescript
* const removeRefsResponse = await client.removeExternalReferences('package-uuid', {
* referenceIds: ['LOCAL-ID-12345']
* });
*
* if (removeRefsResponse.success) {
* console.log('References removed:', removeRefsResponse.message);
* }
* ```
*/
async removeExternalReferences(packageId, options) {
try {
const response = await this.apiClient.delete(`/operations/packages/${packageId}/references`, { data: options });
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to remove external references');
}
}
// Processing Methods
/**
* Executes a specific prompt configuration on package documents.
*
* @param packageId - The unique identifier for the package
* @param options - The processing execution options
* @returns A promise that resolves to the execute processing response
*
* @example
* ```typescript
* const processingResponse = await client.executeProcessing('package-uuid', {
* configId: 'vehicle-info-extraction',
* documentUuids: [] // Optional: process specific documents only
* });
*
* console.log('Processing ID:', processingResponse.processingId);
* console.log('Status:', processingResponse.status); // Always 'queued' initially
* ```
*/
async executeProcessing(packageId, options) {
try {
const requestBody = {
configId: options.configId,
documentUuids: options.documentUuids || []
};
const response = await this.apiClient.post(`/operations/packages/${packageId}/processing/execute`, requestBody);
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to execute processing');
}
}
/**
* Retrieves all processing history for a package.
*
* @param packageId - The unique identifier for the package
* @param options - Optional parameters for the request
* @returns A promise that resolves to the processing history
*
* @example
* ```typescript
* const history = await client.getProcessingHistory('package-uuid-here');
*
* if (history.success && history.data) {
* console.log('Total processing attempts:', history.data.total);
* history.data.processingHistory.forEach(entry => {
* console.log(`Processing ${entry.processingId}: ${entry.status}`);
* });
* }
*
* // Get history with results included
* const historyWithResults = await client.getProcessingHistory('package-uuid-here', {
* includeResults: true
* });
* ```
*/
async getProcessingHistory(packageId, options) {
try {
const params = options?.includeResults ? { includeResults: 'true' } : undefined;
const response = await this.apiClient.get(`/operations/packages/${packageId}/processing`, { params });
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to get processing history');
}
}
/**
* Retrieves all processing history with results for a package.
* This is a convenience method that automatically includes processing results.
*
* @param packageId - The unique identifier for the package
* @returns A promise that resolves to the processing history with results
*
* @example
* ```typescript
* const results = await client.getProcessingWithResults('package-uuid-here');
*
* if (results.success && results.data) {
* results.data.processingHistory.forEach(entry => {
* if (entry.status === 'completed' && entry.result) {
* console.log('Processing result:', entry.result.data);
* if (entry.result.confidence) {
* console.log('Confidence:', entry.result.confidence);
* }
* }
* });
* }
* ```
*/
async getProcessingWithResults(packageId) {
return this.getProcessingHistory(packageId, { includeResults: true });
}
/**
* Retrieves the status and results of a prompt processing request.
*
* @param packageId - The unique identifier for the package
* @param processingId - The unique identifier for the processing request
* @param pollInterval - Optional interval in ms to poll for results until completed
* @param maxAttempts - Optional maximum number of polling attempts
* @returns A promise that resolves to the processing result
*
* @example
* ```typescript
* // Get processing result once
* const result = await client.getProcessingResult('package-uuid', 'processing-uuid');
*
* // Poll for completion (check every 3 seconds, max 20 attempts)
* const result = await client.getProcessingResult(
* 'package-uuid',
* 'processing-uuid',
* 3000,
* 20
* );
*
* if (result.success && result.data?.status === 'completed') {
* console.log('Processing results:', result.data.result);
* }
* ```
*/
async getProcessingResult(packageId, processingId, pollInterval, maxAttempts) {
if (!pollInterval) {
// Just fetch once without polling
try {
const response = await this.apiClient.get(`/operations/packages/${packageId}/processing/${processingId}`);
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to get processing result');
}
}
// Poll until completion
const interval = pollInterval || 3000; // Default 3 seconds
const attempts = maxAttempts || 20; // Default 20 attempts (roughly 1 minute)
return new Promise((resolve, reject) => {
let attemptCount = 0;
const poll = async () => {
attemptCount++;
try {
const response = await this.apiClient.get(`/operations/packages/${packageId}/processing/results/${processingId}`);
const result = response.data;
// Check if processing is complete
if (result.success && result.data) {
const status = result.data.status;
if (status === 'completed' || status === 'failed') {
resolve(result);
return;
}
}
// Continue polling if not complete
if (attemptCount >= attempts) {
reject(new VisionFiError(`Maximum polling attempts (${attempts}) reached. Processing may still be in progress.`, 408, 'polling_timeout'));
return;
}
setTimeout(poll, interval);
}
catch (error) {
reject(this.handleError(error, 'Error while polling for processing results'));
}
};
poll();
});
}
// Product Management Methods
/**
* Retrieves available product types.
*
* @returns A promise that resolves to the product types response
*
* @example
* ```typescript
* const productTypesResponse = await client.getProductTypes();
*
* if (productTypesResponse.success) {
* console.log('Available product types:');
* productTypesResponse.data?.forEach(productType => {
* console.log(`- ${productType.id}: ${productType.name}`);
* });
* }
* ```
*/
async getProductTypes() {
try {
const response = await this.apiClient.get('/admin/products/types');
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to get product types');
}
}
/**
* Retrieves a rendered HTML view for a completed processing result.
*
* Prerequisites:
* - Processing status must be 'completed'
* - `hasViews` must be true in the processing result
* - `configCollectionId` and `configVersionId` must be present
* - Processing result must contain artifacts with data
*
* @param packageId - The unique identifier for the package
* @param processingId - The unique identifier for the processing request
* @param options - Optional parameters including viewName
* @returns A promise that resolves to the processing view response with base64-encoded HTML
*
* @example
* ```typescript
* // Assuming you've already checked that hasViews is true from getProcessingResult
* // Get the default view
* const viewResponse = await client.getProcessingView(
* 'package-uuid',
* 'processing-uuid'
* );
*
* if (viewResponse.success && viewResponse.view) {
* // Decode the base64 HTML
* const html = Buffer.from(viewResponse.view, 'base64').toString('utf-8');
*
* // Save to file
* fs.writeFileSync('result.html', html);
*
* // Or use in your application
* console.log('HTML content retrieved successfully');
* }
*
* // Use a specific template
* const viewResponse = await client.getProcessingView(
* 'package-uuid',
* 'processing-uuid',
* { viewName: 'custom-template' }
* );
* ```
*/
async getProcessingView(packageId, processingId, options) {
try {
const params = options?.viewName ? { viewName: options.viewName } : undefined;
const response = await this.apiClient.get(`/operations/packages/${packageId}/processing/${processingId}/view`, { params });
return response.data;
}
catch (error) {
throw this.handleError(error, 'Failed to get processing view');
}
}
}