{"_id":"@besales/loghub-client","_rev":"4-c3b273929504c6a1c670839967088293","name":"@besales/loghub-client","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.0":{"name":"@besales/loghub-client","version":"1.0.0","keywords":["logging","error-tracking","telegram"],"license":"MIT","_id":"@besales/loghub-client@1.0.0","maintainers":[{"name":"peresvets","email":"nick.soloview@gmail.com"}],"dist":{"shasum":"aa9cc0ce531e94057dd44fbf199997ab700c52ef","tarball":"https://registry.npmjs.org/@besales/loghub-client/-/loghub-client-1.0.0.tgz","fileCount":7,"integrity":"sha512-9P5qFm8XDgXsXayCEihj70QVmSzM4xJwrTZXOBx7vp1sAx9M7QGtelhuFpfSFh61QSZ5ol6wXKLDKrn9tZJvyg==","signatures":[{"sig":"MEQCIDul/WgIPP4BgfDp6AXSiVRYLPJxxi5Vk8fR9FHhFRl2AiAO47gZ0yY+DZYylGWoR/kTq2JHdGusL2XowrMNRPr3/g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":27834},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"9988c83e5945c24937bd7b638803379336035f65","scripts":{"build":"tsc -p tsconfig.build.json","prepublishOnly":"yarn build"},"_npmUser":{"name":"peresvets","email":"nick.soloview@gmail.com"},"description":"Lightweight TypeScript client for LogHub error logging service","directories":{},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/loghub-client_1.0.0_1773512440233_0.2235225320758787","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@besales/loghub-client","version":"1.0.1","keywords":["logging","error-tracking","telegram"],"license":"MIT","_id":"@besales/loghub-client@1.0.1","maintainers":[{"name":"peresvets","email":"nick.soloview@gmail.com"}],"dist":{"shasum":"4d9be3f9875a46ce8d6b368e80abe3cee08f19d4","tarball":"https://registry.npmjs.org/@besales/loghub-client/-/loghub-client-1.0.1.tgz","fileCount":7,"integrity":"sha512-7jCMd5t+qCWHFwu3A19qtGNiJTpdEj+RUAZk21p/U5lDa9drxTA+hDyNQxTMhcFVJNlxMmB19hq7ms4huWDBJw==","signatures":[{"sig":"MEYCIQDh9lmoidPozhZ8V+YzkzwStfiHs3ZBvFzBNOiHjGN/QwIhANfKnUTZLYbzqwQxtTLY/HoWkeEhTXn9+7tFpNkQk5uH","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":27862},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"b0c7a5ba57a7fecec70940dbb7ce0890812f327e","scripts":{"build":"tsc -p tsconfig.build.json","prepublishOnly":"yarn build"},"_npmUser":{"name":"peresvets","email":"nick.soloview@gmail.com"},"description":"Lightweight TypeScript client for LogHub error logging service","directories":{},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/loghub-client_1.0.1_1773515272005_0.6727971902609979","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@besales/loghub-client","version":"1.1.0","description":"Lightweight TypeScript client for LogHub error logging service","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc -p tsconfig.build.json","prepublishOnly":"yarn build"},"engines":{"node":">=18.0.0"},"keywords":["logging","error-tracking","telegram"],"license":"MIT","_id":"@besales/loghub-client@1.1.0","gitHead":"453b4c916811255e5c271e1ad6f8ed1408d5cb13","_nodeVersion":"22.22.2","_npmVersion":"10.9.7","dist":{"integrity":"sha512-TrNOKMLfyVYs28QaRP/H24lDEwzOPX+9WtlVQyj9omZQn204e7HCZxDcCxI2kVQONRCr2p2aB/wojbq8A3C8zA==","shasum":"554bd5cd652b0a0ecb13855c5e58ea5d59cbf151","tarball":"https://registry.npmjs.org/@besales/loghub-client/-/loghub-client-1.1.0.tgz","fileCount":7,"unpackedSize":30325,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCID7lQi5rpXGW6NvnM2FedC27wIO/UgZv/cxUnD7k8EuxAiEAs5oFfKuct7NTJW0O9a3kHIVha7Y7wZ78p95CIg7j+YI="}]},"_npmUser":{"name":"peresvets","email":"nick.soloview@gmail.com"},"directories":{},"maintainers":[{"name":"peresvets","email":"nick.soloview@gmail.com"},{"name":"solgalovk","email":"kirillsolgalov@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/loghub-client_1.1.0_1780082060386_0.15826557449121048"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-14T18:20:40.123Z","modified":"2026-05-29T19:14:20.706Z","1.0.0":"2026-03-14T18:20:40.387Z","1.0.1":"2026-03-14T19:07:52.132Z","1.1.0":"2026-05-29T19:14:20.587Z"},"license":"MIT","keywords":["logging","error-tracking","telegram"],"description":"Lightweight TypeScript client for LogHub error logging service","maintainers":[{"name":"peresvets","email":"nick.soloview@gmail.com"},{"name":"solgalovk","email":"kirillsolgalov@gmail.com"}],"readme":"# @besales/loghub-client\n\nЛёгкий TypeScript-клиент для отправки логов в [LogHub](https://github.com/be-sales/loghub). Ноль внешних зависимостей — только нативный `fetch` (Node.js >= 18).\n\n## Установка\n\n```bash\nyarn add @besales/loghub-client\n# или\nnpm install @besales/loghub-client\n```\n\n## Quick Start\n\n```typescript\nimport { LogHubClient } from '@besales/loghub-client';\n\nconst loghub = new LogHubClient({\n  endpoint: 'https://loghub.example.com',\n  apiKey: 'sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // ← ваш API-ключ\n});\n\nawait loghub.error('Cannot connect to database', {\n  stackTrace: error.stack,\n  metadata: { userId: 'usr_123', requestId: 'req_456' },\n});\n```\n\n## API\n\n### Конструктор\n\n```typescript\nconst client = new LogHubClient(options: LogHubClientOptions);\n```\n\n| Параметр | Тип | По умолчанию | Описание |\n|----------|-----|-------------|----------|\n| `endpoint` | `string` | — | URL LogHub-сервиса (trailing slash удаляется) |\n| `apiKey` | `string` | — | API-ключ сервиса (формат `sk_live_...`) |\n| `timeout` | `number` | `5000` | Таймаут HTTP-запроса в мс |\n| `retries` | `number` | `2` | Количество повторных попыток при сетевых ошибках |\n| `retryDelay` | `number` | `1000` | Базовая задержка между retry в мс |\n| `onError` | `(error: Error) => void` | `console.error` | Callback при ошибке отправки |\n| `throwOnError` | `boolean` | `false` | Бросать ли исключение при неудаче. По умолчанию метод вернёт `null` |\n\n> По умолчанию логирование **не бросает исключений** — `await loghub.error(...)` безопасен и не уронит ваш код. При неудаче вызывается `onError`, метод возвращает `null`.\n\n### Методы\n\n```typescript\nclass LogHubClient {\n  /** Отправка лога с произвольным уровнем. null при ошибке (если throwOnError=false) */\n  log(level: LogLevel, message: string, options?: LogOptions): Promise<LogResponse | null>;\n\n  /** Shorthand-методы */\n  debug(message: string, options?: LogOptions): Promise<LogResponse | null>;\n  info(message: string, options?: LogOptions): Promise<LogResponse | null>;\n  warn(message: string, options?: LogOptions): Promise<LogResponse | null>;\n  error(message: string, options?: LogOptions): Promise<LogResponse | null>;\n  fatal(message: string, options?: LogOptions): Promise<LogResponse | null>;\n}\n```\n\n### LogOptions\n\n```typescript\ninterface LogOptions {\n  /** Stack trace ошибки */\n  stackTrace?: string;\n  /** Произвольные метаданные */\n  metadata?: Record<string, unknown>;\n}\n```\n\n### LogResponse\n\n```typescript\ninterface LogResponse {\n  /** ID созданного лога */\n  id: string;\n  /** Fingerprint ошибки (SHA-256) */\n  fingerprint: string;\n  /** Был ли лог дедуплицирован */\n  deduplicated: boolean;\n}\n```\n\n### LogLevel\n\n```typescript\nenum LogLevel {\n  DEBUG = 'DEBUG',\n  INFO = 'INFO',\n  WARN = 'WARN',\n  ERROR = 'ERROR',\n  FATAL = 'FATAL',\n}\n```\n\n| Уровень | Когда использовать |\n|---------|-------------------|\n| `DEBUG` | Детальные данные для диагностики |\n| `INFO` | Значимые события (запуск, миграция) |\n| `WARN` | Потенциальные проблемы (высокая нагрузка) |\n| `ERROR` | Ошибки, требующие внимания |\n| `FATAL` | Сбой, требующий немедленного вмешательства |\n\n### LogHubApiError\n\n```typescript\nclass LogHubApiError extends Error {\n  readonly statusCode: number;\n  readonly responseBody: string;\n}\n```\n\nВыбрасывается при HTTP-ошибках сервера. Содержит код ответа и тело.\n\n## NestJS интеграция\n\n### Модуль\n\n```typescript\nimport { Global, Module } from '@nestjs/common';\nimport { ConfigService } from '@nestjs/config';\nimport { LogHubClient } from '@besales/loghub-client';\n\nconst LOGHUB_CLIENT = 'LOGHUB_CLIENT';\n\n@Global()\n@Module({\n  providers: [\n    {\n      provide: LOGHUB_CLIENT,\n      useFactory: (config: ConfigService) => {\n        return new LogHubClient({\n          endpoint: config.getOrThrow('LOGHUB_ENDPOINT'),\n          apiKey: config.getOrThrow('LOGHUB_API_KEY'),\n        });\n      },\n      inject: [ConfigService],\n    },\n  ],\n  exports: [LOGHUB_CLIENT],\n})\nexport class LogHubModule {}\n```\n\n### Использование в сервисе\n\n```typescript\n@Injectable()\nexport class PaymentService {\n  constructor(\n    @Inject('LOGHUB_CLIENT') private readonly loghub: LogHubClient,\n  ) {}\n\n  async processPayment(dto: PaymentDto): Promise<void> {\n    try {\n      // ... бизнес-логика ...\n    } catch (error) {\n      await this.loghub.error('Payment processing failed', {\n        stackTrace: error instanceof Error ? error.stack : undefined,\n        metadata: { paymentId: dto.id, amount: dto.amount },\n      });\n      throw error;\n    }\n  }\n}\n```\n\n### Глобальный exception filter\n\n```typescript\nimport { ExceptionFilter, Catch, ArgumentsHost, HttpException, Inject } from '@nestjs/common';\nimport { LogHubClient } from '@besales/loghub-client';\n\n@Catch()\nexport class GlobalExceptionLogHubFilter implements ExceptionFilter {\n  constructor(\n    @Inject('LOGHUB_CLIENT') private readonly loghub: LogHubClient,\n  ) {}\n\n  async catch(exception: unknown, host: ArgumentsHost): Promise<void> {\n    const ctx = host.switchToHttp();\n    const request = ctx.getRequest();\n    const response = ctx.getResponse();\n\n    const status = exception instanceof HttpException\n      ? exception.getStatus()\n      : 500;\n\n    // Отправляем в LogHub только 5xx ошибки\n    if (status >= 500) {\n      const error = exception instanceof Error ? exception : new Error(String(exception));\n\n      // Fire-and-forget: не блокируем ответ\n      this.loghub.error(error.message, {\n        stackTrace: error.stack,\n        metadata: {\n          method: request.method,\n          url: request.url,\n          statusCode: status,\n          userAgent: request.headers['user-agent'],\n        },\n      }).catch(() => {\n        // Не падаем, если LogHub недоступен\n      });\n    }\n\n    response.status(status).send({\n      statusCode: status,\n      message: exception instanceof HttpException\n        ? exception.message\n        : 'Internal server error',\n    });\n  }\n}\n```\n\n## Express интеграция\n\n```typescript\nimport express from 'express';\nimport { LogHubClient } from '@besales/loghub-client';\n\nconst app = express();\nconst loghub = new LogHubClient({\n  endpoint: process.env.LOGHUB_ENDPOINT!,\n  apiKey: process.env.LOGHUB_API_KEY!,\n});\n\n// Global error handler (должен быть последним middleware)\napp.use((err: Error, req: express.Request, res: express.Response, _next: express.NextFunction) => {\n  loghub.error(err.message, {\n    stackTrace: err.stack,\n    metadata: {\n      method: req.method,\n      url: req.originalUrl,\n      ip: req.ip,\n    },\n  }).catch(() => {\n    // Не падаем, если LogHub недоступен\n  });\n\n  res.status(500).json({ error: 'Internal server error' });\n});\n```\n\n## Telegram-бот интеграция\n\n```typescript\nimport { Telegraf } from 'telegraf';\nimport { LogHubClient } from '@besales/loghub-client';\n\nconst bot = new Telegraf(process.env.BOT_TOKEN!);\nconst loghub = new LogHubClient({\n  endpoint: process.env.LOGHUB_ENDPOINT!,\n  apiKey: process.env.LOGHUB_API_KEY!,\n});\n\nbot.catch((error, ctx) => {\n  loghub.error(error instanceof Error ? error.message : 'Unknown bot error', {\n    stackTrace: error instanceof Error ? error.stack : undefined,\n    metadata: {\n      updateType: ctx.updateType,\n      chatId: ctx.chat?.id,\n      userId: ctx.from?.id,\n    },\n  }).catch(() => {});\n});\n```\n\n## Обработка ошибок\n\n### Retry-стратегия\n\n- **5xx ошибки** и **сетевые ошибки** — автоматический retry с exponential backoff (`retryDelay * 2^attempt`)\n- **4xx ошибки** — без retry (клиентская ошибка, retry бессмысленен)\n- После исчерпания попыток вызывается `onError`. По умолчанию метод возвращает `null` и **не бросает** — передайте `throwOnError: true`, чтобы пробрасывать исключение\n\n### Fire-and-forget\n\nПо умолчанию ошибки логирования не пробрасываются, поэтому `.catch()` не обязателен:\n\n```typescript\n// безопасно: при сбое LogHub вернёт null, исключение не бросится\nawait loghub.error('Something failed', { stackTrace: error.stack });\n```\n\n### Пользовательский onError\n\n```typescript\nconst loghub = new LogHubClient({\n  endpoint: 'https://loghub.example.com',\n  apiKey: 'sk_live_...',\n  onError: (error) => {\n    // Собственная обработка: Sentry, Winston, etc.\n    myLogger.warn('LogHub unavailable', { error: error.message });\n  },\n});\n```\n\n## Конфигурация\n\n| Параметр | Значение по умолчанию | Описание |\n|----------|----------------------|----------|\n| `timeout` | 5000 мс | Таймаут одного запроса |\n| `retries` | 2 | Макс. повторных попыток |\n| `retryDelay` | 1000 мс | Базовая задержка (удваивается) |\n\n## Лимиты сервера\n\n| Параметр | Значение |\n|----------|----------|\n| `message` | Макс. 2 000 символов |\n| `stackTrace` | Макс. 10 000 символов |\n| `metadata` | Макс. 50 KB (JSON) |\n| Размер тела запроса | Макс. 1 MB |\n| Окно дедупликации | 3 минуты (одинаковые ошибки подавляются) |\n\n## Лицензия\n\nMIT\n","readmeFilename":"README.md"}