solver-sdk
Version:
SDK для интеграции с Code Solver Backend API (совместимо с браузером и Node.js), с поддержкой функциональности мышления (Thinking Mode)
313 lines • 13.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProjectsApi = exports.ApiEndpoints = void 0;
const code_solver_websocket_client_js_1 = require("../utils/code-solver-websocket-client.js");
/**
* Константы для API путей
*/
var ApiEndpoints;
(function (ApiEndpoints) {
ApiEndpoints["PROJECTS"] = "/api/v1/projects";
ApiEndpoints["PROJECT"] = "/api/v1/projects/:id";
ApiEndpoints["PROJECT_INDEXING_STATUS"] = "/api/v1/projects/:id/indexing_status";
ApiEndpoints["PROJECT_CANCEL_INDEXING"] = "/api/v1/projects/:id/cancel_indexing";
ApiEndpoints["PROJECT_CLEAR_ERROR"] = "/api/v1/projects/:id/clear_error";
})(ApiEndpoints || (exports.ApiEndpoints = ApiEndpoints = {}));
/**
* Замещает параметры в пути API
* @param endpoint Шаблон пути API
* @param params Параметры для замещения
* @returns Путь API с замещенными параметрами
*/
function formatEndpoint(endpoint, params) {
let result = endpoint;
for (const [key, value] of Object.entries(params)) {
result = result.replace(`:${key}`, value);
}
return result;
}
/**
* API для работы с проектами
*/
class ProjectsApi {
/**
* Создает новый экземпляр API для работы с проектами
* @param {HttpClient} httpClient HTTP клиент
*/
constructor(httpClient) {
/** WebSocket клиент */
this.wsClient = null;
/** Родительский SDK */
this.parentSdk = null;
this.httpClient = httpClient;
}
/**
* Проверяет валидность идентификатора проекта
* @param {string} projectId ID проекта
* @throws {Error} Если ID проекта не валиден
*/
validateProjectId(projectId) {
if (projectId === undefined || projectId === null) {
throw new Error('Project ID is required');
}
if (typeof projectId !== 'string') {
throw new Error('Project ID must be a string');
}
if (projectId.trim() === '') {
throw new Error('Project ID cannot be empty');
}
}
/**
* Получает список всех проектов
* @returns {Promise<Project[]>} Список проектов
*/
async getAllProjects() {
return this.httpClient.get('/api/v1/projects');
}
/**
* Получает проект по ID
* @param {string} projectId ID проекта
* @returns {Promise<Project>} Проект
*/
async getProject(projectId) {
this.validateProjectId(projectId);
return this.httpClient.get('/api/v1/projects/' + projectId);
}
/**
* Создает новый проект
* @param {string} name Название проекта
* @param {string} path Путь к проекту
* @param {ProjectOptions} [options] Дополнительные опции проекта
* @returns {Promise<Project>} Созданный проект
*/
async createProject(name, path, options) {
return this.httpClient.post('/api/v1/projects', {
name,
path,
...options
});
}
/**
* Обновляет проект
* @param {string} projectId ID проекта
* @param {Partial<ProjectUpdateData>} data Данные для обновления
* @returns {Promise<Project>} Обновленный проект
*/
async updateProject(projectId, data) {
this.validateProjectId(projectId);
return this.httpClient.put('/api/v1/projects/' + projectId, data);
}
/**
* Удаляет проект
* @param {string} projectId ID проекта
* @returns {Promise<void>}
*/
async deleteProject(projectId) {
this.validateProjectId(projectId);
return this.httpClient.delete('/api/v1/projects/' + projectId);
}
/**
* Запускает индексацию проекта
* @param {string} projectId ID проекта
* @returns {Promise<IndexingResponse>} Информация о начатой индексации
*/
async indexProject(projectId) {
this.validateProjectId(projectId);
return this.httpClient.post('/api/v1/projects/' + projectId + '/index');
}
/**
* Получает статус индексации проекта
* @param {string} projectId Идентификатор проекта
* @returns {Promise<any>} Статус индексации проекта
*/
async getIndexingStatus(projectId) {
this.validateProjectId(projectId);
return this.httpClient.get(formatEndpoint(ApiEndpoints.PROJECT_INDEXING_STATUS, { id: projectId }));
}
/**
* Отменяет индексацию проекта
* @param {string} projectId Идентификатор проекта
* @returns {Promise<boolean>} Результат отмены индексации
*/
async cancelIndexing(projectId) {
if (!projectId) {
throw new Error('Project ID is required');
}
return this.httpClient.post(formatEndpoint(ApiEndpoints.PROJECT_CANCEL_INDEXING, { id: projectId }))
.then(response => {
return true;
})
.catch(error => {
throw new Error(`Failed to cancel indexing: ${error.message}`);
});
}
/**
* Очищает ошибку индексации проекта
* @param {string} projectId Идентификатор проекта
* @returns {Promise<boolean>} Результат очистки ошибки
*/
async clearIndexingError(projectId) {
this.validateProjectId(projectId);
return this.httpClient.post(formatEndpoint(ApiEndpoints.PROJECT_CLEAR_ERROR, { id: projectId }))
.then(() => true)
.catch(error => {
throw new Error(`Failed to clear indexing error: ${error.message}`);
});
}
/**
* Обновляет индекс конкретного файла в проекте
* @param {string} projectId ID проекта
* @param {string} filePath Путь к файлу (относительно корня проекта)
* @param {Object} options Опции обновления индекса
* @param {string} [options.content] Содержимое файла (если не указано, будет прочитано с диска)
* @param {boolean} [options.force=false] Принудительная переиндексация, даже если файл не изменился
* @returns {Promise<FileIndexResponse>} Информация об обновленном индексе файла
*/
async updateFileIndex(projectId, filePath, options = {}) {
this.validateProjectId(projectId);
if (!filePath) {
throw new Error('Путь к файлу не может быть пустым');
}
// Кодируем путь для URL, чтобы избежать проблем со специальными символами
const encodedFilePath = encodeURIComponent(filePath);
return this.httpClient.post('/api/v1/projects/' + projectId + '/files/' + encodedFilePath + '/index', options);
}
/**
* Подключается к WebSocket для событий индексации
* @returns {Promise<boolean>} Результат подключения
*/
async connectWebSocket() {
try {
if (!this.parentSdk || typeof this.parentSdk.getWebSocketClient !== 'function') {
throw new Error('Родительский SDK не настроен или не поддерживает WebSocket');
}
this.wsClient = this.parentSdk.getWebSocketClient();
await this.wsClient.connectToIndexing();
return this.wsClient.isConnected('indexing');
}
catch (error) {
console.error('[ProjectsApi] Ошибка при подключении к WebSocket:', error.message);
return false;
}
}
/**
* Отключается от WebSocket для событий индексации
* @returns {Promise<void>}
*/
async disconnectWebSocket() {
if (this.wsClient) {
await this.wsClient.disconnect('indexing');
}
}
/**
* Проверяет, подключен ли WebSocket
* @returns {boolean} Состояние подключения
*/
isWebSocketConnected() {
return this.wsClient ? this.wsClient.isConnected('indexing') : false;
}
/**
* Устанавливает родительский SDK
* @param sdk Родительский SDK
*/
setParent(sdk) {
this.parentSdk = sdk;
}
/**
* Подписывается на событие через WebSocket
* @param event Название события
* @param callback Функция обратного вызова
*/
on(event, callback) {
if (!this.wsClient) {
console.warn('[ProjectsApi] WebSocket не подключен');
return;
}
this.wsClient.on(event, callback);
}
/**
* Отправляет событие через WebSocket
* @param event Название события
* @param data Данные для отправки
*/
emitSocketEvent(event, data) {
if (!this.wsClient) {
console.warn('[ProjectsApi] WebSocket не подключен');
return;
}
this.wsClient.send(code_solver_websocket_client_js_1.WebSocketNamespace.INDEXING, event, data);
}
/**
* Отправляет событие через WebSocket с ожиданием ответа
* @param event Имя события
* @param data Данные для отправки
* @param timeout Таймаут ожидания ответа
* @returns {Promise<any>} Ответ от сервера
*/
async sendSocketEventWithResponse(event, data, timeout = 5000) {
if (!this.wsClient) {
throw new Error('[ProjectsApi] WebSocket не подключен');
}
// Реализуем собственный механизм emitWithAck, если wsClient не поддерживает его
return new Promise((resolve, reject) => {
try {
// Создаем уникальный ID для запроса
const requestId = `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
// Имя события для ответа
const responseEvent = `${event}_response`;
// Обработчик для получения ответа
const responseHandler = (response) => {
if (response && response.requestId === requestId) {
// Очищаем таймер и удаляем обработчик
clearTimeout(timeoutId);
if (this.wsClient) {
this.wsClient.off(responseEvent, responseHandler);
}
// Разрешаем промис с ответом
resolve(response.data || response);
}
};
// Устанавливаем таймаут
const timeoutId = setTimeout(() => {
if (this.wsClient) {
this.wsClient.off(responseEvent, responseHandler);
}
reject(new Error(`Таймаут ожидания ответа на событие ${event}`));
}, timeout);
// Регистрируем обработчик
this.wsClient.on(responseEvent, responseHandler);
// Отправляем событие
this.wsClient.send(code_solver_websocket_client_js_1.WebSocketNamespace.INDEXING, event, {
...data,
requestId
});
}
catch (error) {
reject(error);
}
});
}
/**
* Остановить индексацию проекта через WebSocket
* @param projectId ID проекта
* @returns {Promise<boolean>} Результат операции
*/
async stopIndexing(projectId) {
this.validateProjectId(projectId);
try {
if (!this.wsClient || !this.isWebSocketConnected()) {
// Если WebSocket не доступен, используем REST API
return this.cancelIndexing(projectId);
}
const result = await this.sendSocketEventWithResponse('STOP_INDEXING', { projectId }, 5000);
return result && result.success === true;
}
catch (error) {
console.error('[ProjectsApi] Ошибка при остановке индексации:', error.message);
// Пробуем через REST API как запасной вариант
return this.cancelIndexing(projectId);
}
}
}
exports.ProjectsApi = ProjectsApi;
//# sourceMappingURL=projects-api.js.map