@duckbug/js 0.1.2 → 1.0.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.
Files changed (61) hide show
  1. package/README.md +348 -73
  2. package/dist/cjs/DuckBug/DuckBugHelper.cjs +136 -0
  3. package/dist/cjs/DuckBug/DuckBugProvider.cjs +82 -13
  4. package/dist/cjs/DuckBug/DuckBugService.cjs +158 -17
  5. package/dist/cjs/DuckBug/Pond.cjs +44 -0
  6. package/dist/cjs/DuckBug/ensureEventId.cjs +47 -0
  7. package/dist/cjs/DuckBug/finalizeIngestEvent.cjs +54 -0
  8. package/dist/cjs/DuckBug/index.cjs +18 -2
  9. package/dist/cjs/DuckBug/parseDuckBugDsn.cjs +82 -0
  10. package/dist/cjs/DuckBug/sanitizeIngestPayload.cjs +67 -0
  11. package/dist/cjs/DuckBug/stripIngestSections.cjs +43 -0
  12. package/dist/cjs/DuckBug/transportTypes.cjs +18 -0
  13. package/dist/cjs/SDK/DuckSDK.cjs +131 -12
  14. package/dist/cjs/SDK/index.cjs +5 -2
  15. package/dist/cjs/contract/index.cjs +18 -0
  16. package/dist/cjs/contract/ingestEvents.cjs +18 -0
  17. package/dist/cjs/index.cjs +24 -6
  18. package/dist/cjs/integrations/node.cjs +54 -0
  19. package/dist/cjs/sdkIdentity.cjs +47 -0
  20. package/dist/esm/DuckBug/DuckBugHelper.js +99 -0
  21. package/dist/esm/DuckBug/DuckBugProvider.js +82 -13
  22. package/dist/esm/DuckBug/DuckBugService.js +156 -15
  23. package/dist/esm/DuckBug/Pond.js +10 -0
  24. package/dist/esm/DuckBug/ensureEventId.js +13 -0
  25. package/dist/esm/DuckBug/finalizeIngestEvent.js +20 -0
  26. package/dist/esm/DuckBug/index.js +5 -1
  27. package/dist/esm/DuckBug/parseDuckBugDsn.js +36 -0
  28. package/dist/esm/DuckBug/sanitizeIngestPayload.js +33 -0
  29. package/dist/esm/DuckBug/stripIngestSections.js +9 -0
  30. package/dist/esm/DuckBug/transportTypes.js +0 -0
  31. package/dist/esm/SDK/DuckSDK.js +127 -11
  32. package/dist/esm/SDK/index.js +2 -2
  33. package/dist/esm/contract/index.js +0 -0
  34. package/dist/esm/contract/ingestEvents.js +0 -0
  35. package/dist/esm/index.js +2 -0
  36. package/dist/esm/integrations/node.js +20 -0
  37. package/dist/esm/sdkIdentity.js +7 -0
  38. package/dist/types/DuckBug/DuckBugConfig.d.ts +28 -0
  39. package/dist/types/DuckBug/DuckBugHelper.d.ts +22 -0
  40. package/dist/types/DuckBug/DuckBugProvider.d.ts +12 -1
  41. package/dist/types/DuckBug/DuckBugService.d.ts +30 -8
  42. package/dist/types/DuckBug/Log.d.ts +1 -7
  43. package/dist/types/DuckBug/Pond.d.ts +9 -0
  44. package/dist/types/DuckBug/ensureEventId.d.ts +4 -0
  45. package/dist/types/DuckBug/finalizeIngestEvent.d.ts +11 -0
  46. package/dist/types/DuckBug/index.d.ts +7 -1
  47. package/dist/types/DuckBug/parseDuckBugDsn.d.ts +17 -0
  48. package/dist/types/DuckBug/sanitizeIngestPayload.d.ts +4 -0
  49. package/dist/types/DuckBug/stripIngestSections.d.ts +4 -0
  50. package/dist/types/DuckBug/transportTypes.d.ts +7 -0
  51. package/dist/types/SDK/DuckSDK.d.ts +31 -4
  52. package/dist/types/SDK/Provider.d.ts +11 -3
  53. package/dist/types/SDK/index.d.ts +3 -2
  54. package/dist/types/contract/index.d.ts +1 -0
  55. package/dist/types/contract/ingestEvents.d.ts +58 -0
  56. package/dist/types/index.d.ts +2 -0
  57. package/dist/types/integrations/node.d.ts +12 -0
  58. package/dist/types/sdkIdentity.d.ts +7 -0
  59. package/package.json +23 -7
  60. package/schemas/error-event.schema.json +147 -0
  61. package/schemas/log-event.schema.json +126 -0
package/README.md CHANGED
@@ -31,76 +31,234 @@ pnpm add @duckbug/js
31
31
 
32
32
  ### Basic Usage
33
33
 
34
+ DSN must follow the ingest URL shape from the DuckBug SDK spec (`duckbug-sdk-spec`): `{origin}/ingest/{projectId}:{publicKey}` or `{origin}/api/ingest/{projectId}:{publicKey}` (for example on `duckbug.io`).
35
+
34
36
  ```typescript
35
- import { DuckSDK, DuckBugProvider } from '@duckbug/js';
37
+ import { Duck, DuckBugProvider, Pond } from '@duckbug/js';
38
+
39
+ const dsn = 'https://api.duckbug.io/ingest/your-project-id:your-public-key';
40
+ const { extraSensitiveKeys } = Pond.ripple(['custom_secret']);
41
+
42
+ const duck = new Duck(
43
+ [new DuckBugProvider({ dsn, extraSensitiveKeys })],
44
+ {
45
+ logReports: {
46
+ log: false,
47
+ warn: true,
48
+ error: true,
49
+ },
50
+ },
51
+ );
52
+
53
+ // Branded + idiomatic error capture
54
+ const err = new Error('Something failed');
55
+ duck.quack('checkout_failed', err);
56
+ duck.captureException(err, 'checkout_failed');
57
+
58
+ // Logging (levels normalized to DEBUG, INFO, WARN, ERROR, FATAL)
59
+ duck.warn('Slow query', { ms: 1200 });
60
+ ```
36
61
 
37
- // Initialize with DuckBug.io provider
38
- const providers = [
39
- new DuckBugProvider({
40
- dsn: 'your-duckbug-dsn-here'
41
- })
42
- ];
62
+ `DuckSDK` remains available as an alias of the same runtime; prefer `Duck` for new code.
43
63
 
44
- // Create SDK instance with optional configuration
45
- const duck = new DuckSDK(providers, {
46
- logReports: {
47
- log: false,
48
- warn: true,
49
- error: true,
50
- }
64
+ Before process exit, await `duck.flush()` so queued HTTP work finishes (for example at the end of an `async main()`).
65
+
66
+ ## Full usage example
67
+
68
+ End-to-end pattern for a Node/Bun service: DSN from env, scope (release, user, fingerprint), privacy pipeline, `beforeSend`, batched transport with retries, transport errors, console forwarding, global error handlers, structured logs, manual errors, and clean shutdown.
69
+
70
+ ```typescript
71
+ import {
72
+ Duck,
73
+ DuckBugProvider,
74
+ Pond,
75
+ registerNodeGlobalErrorHandlers,
76
+ } from "@duckbug/js";
77
+
78
+ const dsn = process.env.DUCKBUG_DSN;
79
+ if (!dsn) {
80
+ throw new Error("Set DUCKBUG_DSN to your ingest URL");
81
+ }
82
+
83
+ const { extraSensitiveKeys } = Pond.ripple([
84
+ "custom_secret",
85
+ "internalToken",
86
+ ]);
87
+
88
+ // Transport + ingest errors. Privacy (strip / sanitize / eventId / beforeSend) is applied in `Duck` when you use the core client.
89
+ const duckBug = new DuckBugProvider({
90
+ dsn,
91
+ transport: {
92
+ maxBatchSize: 25,
93
+ maxRetries: 2,
94
+ retryDelayMs: 200,
95
+ },
96
+ onTransportError: (info) => {
97
+ console.error("[duckbug transport]", info.message, info.kind, info.itemCount);
98
+ },
99
+ });
100
+
101
+ const duck = new Duck(
102
+ [duckBug],
103
+ {
104
+ logReports: {
105
+ log: true,
106
+ warn: true,
107
+ error: true,
108
+ },
109
+ },
110
+ {
111
+ extraSensitiveKeys,
112
+ // Omit whole sections before sanitize (see StrippableIngestSection in types)
113
+ stripSections: ["cookies", "headers"],
114
+ beforeSend: async (arg) => {
115
+ // Drop PII-heavy events in dev, or tweak payload
116
+ if (process.env.NODE_ENV === "test") {
117
+ return null;
118
+ }
119
+ if (arg.kind === "log" && arg.event.message.includes("healthcheck")) {
120
+ return null;
121
+ }
122
+ return arg.event;
123
+ },
124
+ },
125
+ );
126
+
127
+ // Applied to every subsequent log / error (merge into event)
128
+ duck.setScope({
129
+ release: "my-app@1.4.2",
130
+ environment: process.env.NODE_ENV ?? "development",
131
+ service: "checkout-api",
132
+ fingerprint: "checkout-api-default",
133
+ // Prefer nesting session-like data under context; top-level `session` may be rejected by ingest.
134
+ context: { deployment: "eu-west" },
51
135
  });
52
136
 
53
- // Start logging
54
- duck.log('Info message', { userId: 123, action: 'user_login' });
55
- duck.debug('Debug message', { debugInfo: 'Connection established' });
56
- duck.warn('Warning message', { warning: 'Rate limit approaching' });
57
- duck.error('Error message', { error: 'Database connection failed' });
58
- duck.fatal('Fatal message', { error: 'Ay, caramba' });
137
+ const unregisterGlobals = registerNodeGlobalErrorHandlers({
138
+ duck,
139
+ rejectionTag: "unhandledRejection",
140
+ exceptionTag: "uncaughtException",
141
+ });
59
142
 
60
- //Send error
61
- const testError = new Error("Integration test error");
62
- testError.stack =
63
- "Error: Integration test error\n at integration.test.ts:1:1";
143
+ async function main() {
144
+ duck.debug("boot", { pid: process.pid });
64
145
 
65
- // Use quack method directly on provider
66
- duckBugProvider.quack("INTEGRATION_ERROR", testError);
146
+ try {
147
+ await doCheckout();
148
+ } catch (e) {
149
+ const err = e instanceof Error ? e : new Error(String(e));
150
+ duck.quack("checkout_failed", err);
151
+ duck.captureException(err, "checkout_failed");
152
+ }
153
+
154
+ duck.warn("slow_query", { table: "orders", ms: 850 });
155
+ duck.error("inventory_low", { sku: "SKU-12", qty: 2 });
156
+ duck.fatal("migration_required", { from: "v10", to: "v11" });
157
+
158
+ await duck.flush();
159
+ }
160
+
161
+ async function doCheckout() {
162
+ // ...
163
+ }
164
+
165
+ main()
166
+ .catch((e) => {
167
+ console.error(e);
168
+ process.exitCode = 1;
169
+ })
170
+ .finally(async () => {
171
+ unregisterGlobals();
172
+ await duck.flush();
173
+ });
174
+ ```
175
+
176
+ **Direct provider (no `Duck`)** — same privacy defaults via `finalizeIngestEvent` inside the provider; you call `sendLog` / `sendError` with `DuckBugLogEvent` / `DuckBugErrorEvent` shapes:
177
+
178
+ ```typescript
179
+ import { DuckBugProvider, logLevel } from "@duckbug/js";
180
+
181
+ const provider = new DuckBugProvider({ dsn });
182
+ provider.sendLog({
183
+ time: Date.now(),
184
+ level: logLevel.INFO,
185
+ message: "Job finished",
186
+ platform: "node",
187
+ dTags: ["worker", "nightly"],
188
+ });
189
+ await provider.flush();
67
190
  ```
68
191
 
69
192
  ## API Reference
70
193
 
71
- ### DuckSDK
194
+ ### Duck / DuckSDK
72
195
 
73
- The main SDK class that manages logging across multiple providers.
196
+ The main SDK class fans out canonical log and error events to all registered providers.
74
197
 
75
198
  #### Constructor
76
199
 
77
200
  ```typescript
78
- new DuckSDK(providers: Provider[], config?: LogProviderConfig)
201
+ new Duck(
202
+ providers: Provider[],
203
+ logProviderConfig?: LogProviderConfig,
204
+ options?: DuckSDKOptions,
205
+ )
79
206
  ```
80
207
 
81
208
  - `providers`: Array of provider instances
82
- - `config`: Optional configuration for log reporting levels
209
+ - `logProviderConfig`: Optional configuration for console interception (`LogProvider`)
210
+ - `options`: Optional `beforeSend`, `stripSections`, `extraSensitiveKeys` (strip → sanitize → `eventId` → `beforeSend` → providers; matches `duckbug-sdk-spec`)
83
211
 
84
212
  #### Methods
85
213
 
86
- - `log(tag: string, payload?: object)`: Log an info-level message
87
- - `debug(tag: string, payload?: object)`: Log a debug-level message
88
- - `warn(tag: string, payload?: object)`: Log a warning-level message
89
- - `error(tag: string, payload?: object)`: Log an error-level message
90
- - `fatal(tag: string, payload?: object)`: Log an fatal-level message
91
- - `quack(tag: string, error: Error)`: Report error
214
+ - `log` / `debug` / `warn` / `error` / `fatal(tag, payload?)`: structured logs
215
+ - `quack(tag, error)`: branded manual error capture; tag is sent as `dTags`, message comes from `error.message`
216
+ - `captureException(error, tag?)`: idiomatic alias for `quack` (default tag `error`)
217
+ - `setScope(partial)`: merge shared metadata into subsequent events
218
+ - `flush()`: await transport drains on providers that implement `flush` (for example `DuckBugProvider`)
219
+
220
+ Each captured log/error gets a UUID `eventId` when omitted (idempotency / retries).
92
221
 
93
222
  ### DuckBugProvider
94
223
 
95
- The official DuckBug.io provider for sending logs to the DuckBug.io platform.
224
+ First-party provider: posts JSON to single-event ingest by default, or to `/logs/batch` and `/errors/batch` when `transport.maxBatchSize > 1` (body is a JSON array, as required by the DuckBug API).
96
225
 
97
226
  #### Constructor
98
227
 
99
228
  ```typescript
100
- new DuckBugProvider(config: DuckConfig)
229
+ new DuckBugProvider({
230
+ dsn: string,
231
+ extraSensitiveKeys?: string[],
232
+ stripSections?: StrippableIngestSection[],
233
+ beforeSend?: (arg) => event | null | undefined | Promise<...>,
234
+ transport?: {
235
+ maxBatchSize?: number; // default 1 — one POST per event
236
+ maxRetries?: number;
237
+ retryDelayMs?: number;
238
+ fetchImpl?: typeof fetch;
239
+ },
240
+ onTransportError?: (info: TransportFailureInfo) => void,
241
+ })
242
+ // or
243
+ DuckBugProvider.fromDSN(dsn)
101
244
  ```
102
245
 
103
- - `config.dsn`: Your DuckBug.io DSN (Data Source Name)
246
+ - `config.dsn`: full ingest URL, e.g. `https://api.duckbug.io/ingest/myProject:myKey`
247
+ - `flush()`: returns a `Promise` that resolves when queued requests for this provider have been sent
248
+
249
+ ### Privacy, batching, and Node hooks
250
+
251
+ - **Strip sections**: omit whole request fields (`headers`, `cookies`, `session`, …) before sanitize via `stripSections` on `DuckBugProvider` or `DuckSDK` options.
252
+ - **`beforeSend`**: on `Duck` / `DuckSDK` for all providers; on `DuckBugProvider` when using the provider without the core client. Return `null` to drop an event.
253
+ - **Node global errors** (optional, no core framework deps):
254
+
255
+ ```typescript
256
+ import { Duck, DuckBugProvider, registerNodeGlobalErrorHandlers } from '@duckbug/js';
257
+
258
+ const duck = new Duck([DuckBugProvider.fromDSN(dsn)]);
259
+ const unregister = registerNodeGlobalErrorHandlers({ duck });
260
+ // ... on shutdown: unregister();
261
+ ```
104
262
 
105
263
  ### Log Provider Configuration
106
264
 
@@ -116,62 +274,54 @@ type LogProviderConfig = {
116
274
 
117
275
  ## Custom Providers
118
276
 
119
- You can create custom providers by implementing the `Provider` interface:
277
+ Implement `Provider`: handle canonical `DuckBugLogEvent` / `DuckBugErrorEvent` from `sendLog` / `sendError` (optional second argument `SendEventMeta` when events are already finalized in `DuckSDK`), and optional console-style methods for `LogProvider` hooks.
120
278
 
121
279
  ```typescript
122
- import { Provider, LogLevel } from '@duckbug/js';
280
+ import type {
281
+ DuckBugErrorEvent,
282
+ DuckBugLogEvent,
283
+ Provider,
284
+ } from '@duckbug/js';
123
285
 
124
286
  class TelegramProvider implements Provider {
125
287
  constructor(private botToken: string, private chatId: string) {}
126
288
 
127
- log(...args: unknown[]): void {
128
- this.sendToTelegram('📝', args);
289
+ sendLog(event: DuckBugLogEvent): void {
290
+ this.sendToTelegram('📝', `${event.level} ${event.message}`);
129
291
  }
130
292
 
131
- warn(...args: unknown[]): void {
132
- this.sendToTelegram('⚠️', args);
293
+ sendError(event: DuckBugErrorEvent): void {
294
+ this.sendToTelegram('💀', event.message);
133
295
  }
134
296
 
135
- error(...args: unknown[]): void {
136
- this.sendToTelegram('🚨', args);
297
+ log(...args: unknown[]): void {
298
+ this.sendToTelegram('📝', String(args[0]));
137
299
  }
138
300
 
139
- report(tag: string, level: LogLevel, payload?: object): void {
140
- const emojiMap: Record<LogLevel, string> = {
141
- INFO: '📝',
142
- DEBUG: '🦆',
143
- WARN: '⚠️',
144
- ERROR: '🚨',
145
- FATAL: '💀',
146
- };
147
- this.sendToTelegram(emojiMap[level], [tag, payload]);
301
+ warn(...args: unknown[]): void {
302
+ this.sendToTelegram('⚠️', String(args[0]));
148
303
  }
149
304
 
150
- quack(tag: string, error: Error): void {
151
- this.sendToTelegram('💀', [tag, error.message]);
305
+ error(...args: unknown[]): void {
306
+ this.sendToTelegram('🚨', String(args[0]));
152
307
  }
153
308
 
154
- private sendToTelegram(emoji: string, args: unknown[]) {
155
- const message = `${emoji} ${args.join(' ')}`;
156
- // Implementation to send message to Telegram
309
+ private sendToTelegram(emoji: string, text: string) {
310
+ const message = `${emoji} ${text}`;
157
311
  fetch(`https://api.telegram.org/bot${this.botToken}/sendMessage`, {
158
312
  method: 'POST',
159
313
  headers: { 'Content-Type': 'application/json' },
160
- body: JSON.stringify({
161
- chat_id: this.chatId,
162
- text: message
163
- })
314
+ body: JSON.stringify({ chat_id: this.chatId, text: message }),
164
315
  });
165
316
  }
166
317
  }
167
318
 
168
- // Usage
169
319
  const providers = [
170
- new DuckBugProvider({ dsn: 'your-dsn' }),
171
- new TelegramProvider('your-bot-token', 'your-chat-id')
320
+ DuckBugProvider.fromDSN('https://api.duckbug.io/ingest/project:key'),
321
+ new TelegramProvider('your-bot-token', 'your-chat-id'),
172
322
  ];
173
323
 
174
- const duck = new DuckSDK(providers);
324
+ const duck = new Duck(providers);
175
325
  ```
176
326
 
177
327
  ## Development
@@ -181,7 +331,7 @@ const duck = new DuckSDK(providers);
181
331
  Install dependencies:
182
332
 
183
333
  ```bash
184
- yarn install
334
+ bun install
185
335
  ```
186
336
 
187
337
  ### Build
@@ -189,7 +339,7 @@ yarn install
189
339
  Build the library:
190
340
 
191
341
  ```bash
192
- yarn build
342
+ bun run build
193
343
  ```
194
344
 
195
345
  ### Linting
@@ -197,15 +347,140 @@ yarn build
197
347
  Run linting:
198
348
 
199
349
  ```bash
200
- yarn lint
350
+ bun run lint
351
+ ```
352
+
353
+ ### Commit Messages
354
+
355
+ Этот проект использует [Conventional Commits](https://www.conventionalcommits.org/) для стандартизации сообщений коммитов. Все коммиты должны соответствовать следующему формату:
356
+
357
+ ```
358
+ <type>(<scope>): <subject>
359
+
360
+ <body>
361
+
362
+ <footer>
363
+ ```
364
+
365
+ #### Типы коммитов (обязательные)
366
+
367
+ - `feat`: Новая функциональность
368
+ - `fix`: Исправление бага
369
+ - `docs`: Изменения в документации
370
+ - `style`: Форматирование кода (не влияет на выполнение кода)
371
+ - `refactor`: Рефакторинг кода
372
+ - `perf`: Улучшение производительности
373
+ - `test`: Добавление или изменение тестов
374
+ - `build`: Изменения в системе сборки или внешних зависимостях
375
+ - `ci`: Изменения в CI конфигурации
376
+ - `chore`: Обновление задач сборки, настроек и т.д.
377
+ - `revert`: Откат предыдущего коммита
378
+
379
+ #### Примеры корректных коммитов
380
+
381
+ ```bash
382
+ feat: добавить поддержку логирования ошибок
383
+ fix: исправить утечку памяти в DuckBugProvider
384
+ docs: обновить README с примерами использования
385
+ test: добавить тесты для DuckSDK
386
+ refactor: улучшить структуру классов провайдеров
387
+ ```
388
+
389
+ #### Проверка коммитов
390
+
391
+ Автоматическая проверка формата коммитов выполняется через git hook. При создании коммита с неправильным форматом вы получите подробное сообщение об ошибке с описанием проблемы и примерами правильного формата.
392
+
393
+ **Примеры ошибок:**
394
+
395
+ ❌ Если забыли указать тип:
201
396
  ```
397
+ ❌ Тип коммита обязателен!
398
+ 📝 Формат коммита: <type>: <описание>
399
+ 💡 Примеры:
400
+ feat: добавить новую функцию
401
+ fix: исправить обработку ошибок
402
+ ```
403
+
404
+ ❌ Если использовали неправильный тип:
405
+ ```
406
+ ❌ Неверный тип коммита!
407
+ ✅ Используйте один из допустимых типов:
408
+ - feat: новая функциональность
409
+ - fix: исправление бага
410
+ ...
411
+ ```
412
+
413
+ Для ручной проверки сообщения коммита:
414
+
415
+ ```bash
416
+ bun run commitlint -- --from HEAD~1 --to HEAD
417
+ ```
418
+
419
+ ### Автоматические релизы
420
+
421
+ Этот проект использует [semantic-release](https://github.com/semantic-release/semantic-release) для автоматического управления версиями и релизами.
422
+
423
+ #### Как это работает:
424
+
425
+ - **Версионирование**: Версия автоматически обновляется на основе типов коммитов:
426
+ - `feat:` → минорное обновление (1.0.0 → 1.1.0)
427
+ - `fix:` → патч (1.0.0 → 1.0.1)
428
+ - `BREAKING CHANGE` или `feat!:` → мажорное обновление (1.0.0 → 2.0.0)
429
+ - `chore:`, `docs:`, `style:` и другие → без релиза
430
+
431
+ - **Автоматические действия при пуше в `main`**:
432
+ 1. Анализ коммитов с последнего релиза
433
+ 2. Определение новой версии
434
+ 3. Генерация CHANGELOG.md
435
+ 4. Обновление версии в package.json
436
+ 5. Создание git тега
437
+ 6. Публикация в npm
438
+ 7. Создание GitHub Release с заметками
439
+
440
+ #### Настройка:
441
+
442
+ 1. **Создайте NPM токен** (только для публикации):
443
+ - Перейдите на https://www.npmjs.com/settings/YOUR_USERNAME/tokens
444
+ - Создайте токен с правами `Automation`
445
+ - Добавьте его в GitHub Secrets как `NPM_TOKEN`
446
+
447
+ 2. **GitHub Actions**:
448
+ - Workflow `release.yaml` автоматически запускается при пуше в `main` или `beta`
449
+ - Использует `GITHUB_TOKEN` (автоматически предоставляется GitHub Actions)
450
+ - Использует `NPM_TOKEN` из секретов для публикации в npm
451
+
452
+ #### Примеры коммитов для релизов:
453
+
454
+ ```bash
455
+ # Патч релиз (1.0.0 → 1.0.1)
456
+ fix: исправить обработку ошибок в DuckBugProvider
457
+
458
+ # Минорный релиз (1.0.0 → 1.1.0)
459
+ feat: добавить поддержку фильтрации логов
460
+
461
+ # Мажорный релиз (1.0.0 → 2.0.0)
462
+ feat!: изменить API провайдеров
463
+
464
+ # или
465
+
466
+ feat: добавить новую функцию
467
+
468
+ BREAKING CHANGE: изменена структура конфигурации DuckBugProvider
469
+ ```
470
+
471
+ **Примечание**: Коммиты без типа или с типом `chore`, `docs`, `style` не создают новый релиз, но могут быть включены в CHANGELOG.
202
472
 
203
473
  ## TypeScript Support
204
474
 
205
475
  This package includes TypeScript definitions. All exports are fully typed:
206
476
 
207
477
  ```typescript
208
- import type { Provider, DuckConfig, LogLevel } from '@duckbug/js';
478
+ import type {
479
+ Provider,
480
+ DuckBugConfig,
481
+ DuckBugLogEvent,
482
+ LogLevel,
483
+ } from "@duckbug/js";
209
484
  ```
210
485
 
211
486
  ## Browser Compatibility
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ processError: ()=>processError,
28
+ parseError: ()=>parseError
29
+ });
30
+ const external_sdkIdentity_cjs_namespaceObject = require("../sdkIdentity.cjs");
31
+ function tryJsonObjectFromMessage(message) {
32
+ const trimmed = message.trim();
33
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null;
34
+ try {
35
+ const v = JSON.parse(message);
36
+ if (null !== v && "object" == typeof v) return v;
37
+ } catch {}
38
+ return null;
39
+ }
40
+ function parseStacktrace(stack) {
41
+ let file = "unknown";
42
+ let line = 0;
43
+ let stacktrace;
44
+ if (stack) {
45
+ const stackLines = stack.split("\n");
46
+ let firstStackLineWithFile = null;
47
+ for(let i = 1; i < stackLines.length; i++){
48
+ const lineStr = stackLines[i];
49
+ if (-1 !== lineStr.indexOf("at ") && (-1 !== lineStr.indexOf(":") || -1 !== lineStr.indexOf("("))) {
50
+ firstStackLineWithFile = lineStr;
51
+ break;
52
+ }
53
+ }
54
+ if (firstStackLineWithFile) {
55
+ const match = firstStackLineWithFile.match(/\(([^)]+):(\d+):(\d+)\)/) || firstStackLineWithFile.match(/at\s+.*?\(([^)]+):(\d+):(\d+)\)/) || firstStackLineWithFile.match(/([^:()\s]+):(\d+):(\d+)/);
56
+ if (match?.[1]) {
57
+ file = match[1].replace(/^file:\/\//, "").replace(/^\/+/, "");
58
+ line = parseInt(match[2] || "0", 10);
59
+ }
60
+ }
61
+ stacktrace = {
62
+ raw: stack,
63
+ frames: stackLines.filter((lineStr)=>lineStr.trim()).map((lineStr, index)=>({
64
+ index,
65
+ content: lineStr.trim()
66
+ }))
67
+ };
68
+ } else {
69
+ stacktrace = {
70
+ raw: "",
71
+ frames: []
72
+ };
73
+ file = "unknown";
74
+ line = 0;
75
+ }
76
+ return {
77
+ file,
78
+ line,
79
+ stacktrace
80
+ };
81
+ }
82
+ function parseContext(contextStr) {
83
+ if (!contextStr) return null;
84
+ try {
85
+ return JSON.parse(contextStr);
86
+ } catch {
87
+ return {
88
+ message: contextStr
89
+ };
90
+ }
91
+ }
92
+ function parseError(error) {
93
+ const { file, line, stacktrace } = parseStacktrace(error.stack);
94
+ const context = parseContext(error.message);
95
+ return {
96
+ file,
97
+ line,
98
+ stacktrace,
99
+ context
100
+ };
101
+ }
102
+ function processError(error, tag, time) {
103
+ const parsed = parseError(error);
104
+ const message = "string" == typeof error.message && error.message.length > 0 ? error.message : "Error";
105
+ const event = {
106
+ time,
107
+ message,
108
+ stacktrace: parsed.stacktrace,
109
+ stacktraceAsString: error.stack ?? "",
110
+ file: parsed.file,
111
+ line: parsed.line,
112
+ exception: {
113
+ type: error.name,
114
+ message: error.message
115
+ },
116
+ platform: "node",
117
+ sdk: {
118
+ ...external_sdkIdentity_cjs_namespaceObject.SDK_IDENTITY
119
+ }
120
+ };
121
+ if (tag.length > 0) event.dTags = [
122
+ tag
123
+ ];
124
+ const fromJson = tryJsonObjectFromMessage(error.message);
125
+ if (fromJson) event.extra = fromJson;
126
+ return event;
127
+ }
128
+ exports.parseError = __webpack_exports__.parseError;
129
+ exports.processError = __webpack_exports__.processError;
130
+ for(var __webpack_i__ in __webpack_exports__)if (-1 === [
131
+ "parseError",
132
+ "processError"
133
+ ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
134
+ Object.defineProperty(exports, '__esModule', {
135
+ value: true
136
+ });