@observa/sdk 2.4.0 → 2.4.5

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 (79) hide show
  1. package/README.md +140 -176
  2. package/dist/apis/ingestApi.d.ts +1 -15
  3. package/dist/apis/ingestApi.d.ts.map +1 -1
  4. package/dist/apis/ingestApi.js +2 -151
  5. package/dist/apis/ingestApi.js.map +1 -1
  6. package/dist/apis/uptimeApi.js +1 -1
  7. package/dist/apis/uptimeApi.js.map +1 -1
  8. package/dist/domain/ingest.d.ts +4 -33
  9. package/dist/domain/ingest.d.ts.map +1 -1
  10. package/dist/http/httpClient.d.ts +5 -2
  11. package/dist/http/httpClient.d.ts.map +1 -1
  12. package/dist/http/httpClient.js +44 -8
  13. package/dist/http/httpClient.js.map +1 -1
  14. package/dist/index.d.ts +18 -507
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +17 -582
  17. package/dist/index.js.map +1 -1
  18. package/dist/sdk.d.ts +4 -9
  19. package/dist/sdk.d.ts.map +1 -1
  20. package/dist/sdk.js +7 -19
  21. package/dist/sdk.js.map +1 -1
  22. package/package.json +9 -16
  23. package/dist/src/apis/ingestApi.d.ts +0 -22
  24. package/dist/src/apis/ingestApi.js +0 -167
  25. package/dist/src/apis/ingestApi.js.map +0 -1
  26. package/dist/src/apis/uptimeApi.d.ts +0 -11
  27. package/dist/src/apis/uptimeApi.js +0 -39
  28. package/dist/src/apis/uptimeApi.js.map +0 -1
  29. package/dist/src/domain/ingest.d.ts +0 -47
  30. package/dist/src/domain/ingest.js +0 -2
  31. package/dist/src/domain/ingest.js.map +0 -1
  32. package/dist/src/domain/uptime.d.ts +0 -23
  33. package/dist/src/domain/uptime.js +0 -2
  34. package/dist/src/domain/uptime.js.map +0 -1
  35. package/dist/src/http/errors.d.ts +0 -33
  36. package/dist/src/http/errors.js +0 -54
  37. package/dist/src/http/errors.js.map +0 -1
  38. package/dist/src/http/httpClient.d.ts +0 -45
  39. package/dist/src/http/httpClient.js +0 -165
  40. package/dist/src/http/httpClient.js.map +0 -1
  41. package/dist/src/index.d.ts +0 -12
  42. package/dist/src/index.js +0 -7
  43. package/dist/src/index.js.map +0 -1
  44. package/dist/src/sdk.d.ts +0 -23
  45. package/dist/src/sdk.js +0 -40
  46. package/dist/src/sdk.js.map +0 -1
  47. package/dist/src/utils/processContext.d.ts +0 -34
  48. package/dist/src/utils/processContext.js +0 -47
  49. package/dist/src/utils/processContext.js.map +0 -1
  50. package/dist/src/utils/validate.d.ts +0 -2
  51. package/dist/src/utils/validate.js +0 -12
  52. package/dist/src/utils/validate.js.map +0 -1
  53. package/dist/tests/httpClient.test.d.ts +0 -1
  54. package/dist/tests/httpClient.test.js +0 -47
  55. package/dist/tests/httpClient.test.js.map +0 -1
  56. package/dist/tests/ingestApi.test.d.ts +0 -1
  57. package/dist/tests/ingestApi.test.js +0 -65
  58. package/dist/tests/ingestApi.test.js.map +0 -1
  59. package/dist/tests/observaSdk.integration.test.d.ts +0 -1
  60. package/dist/tests/observaSdk.integration.test.js +0 -51
  61. package/dist/tests/observaSdk.integration.test.js.map +0 -1
  62. package/dist/tests/sdk.test.d.ts +0 -1
  63. package/dist/tests/sdk.test.js +0 -104
  64. package/dist/tests/sdk.test.js.map +0 -1
  65. package/dist/tsconfig.build.tsbuildinfo +0 -1
  66. package/dist/utils/processContext.d.ts +0 -35
  67. package/dist/utils/processContext.d.ts.map +0 -1
  68. package/dist/utils/processContext.js +0 -47
  69. package/dist/utils/processContext.js.map +0 -1
  70. package/src/apis/ingestApi.ts +0 -199
  71. package/src/apis/uptimeApi.ts +0 -58
  72. package/src/domain/ingest.ts +0 -93
  73. package/src/domain/uptime.ts +0 -86
  74. package/src/http/errors.ts +0 -88
  75. package/src/http/httpClient.ts +0 -284
  76. package/src/index.ts +0 -68
  77. package/src/sdk.ts +0 -107
  78. package/src/utils/processContext.ts +0 -84
  79. package/src/utils/validate.ts +0 -19
@@ -1,284 +0,0 @@
1
- import { AuthError, NetworkError, TimeoutError, mapHttpError } from './errors'
2
-
3
- /**
4
- * Authentication mode per request.
5
- */
6
- export type AuthMode = 'apiKey' | 'none'
7
- /**
8
- * Supported HTTP methods.
9
- */
10
- export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
11
- /**
12
- * Serializable query parameters.
13
- */
14
- export type QueryParams = Record<string, string | number | boolean | undefined>
15
-
16
- /**
17
- * Retry policy for transient errors.
18
- */
19
- export type RetryPolicy = {
20
- /**
21
- * Number of additional retries.
22
- */
23
- retries: number
24
- /**
25
- * Computes the delay between retries.
26
- */
27
- retryDelayMs?: (attempt: number, response?: Response, error?: unknown) => number
28
- /**
29
- * Determines whether a response or error should be retried.
30
- */
31
- retryOn?: (response?: Response, error?: unknown) => boolean
32
- }
33
-
34
- /**
35
- * Base HTTP client configuration.
36
- */
37
- export type HttpClientOptions = {
38
- /**
39
- * Backend base URL.
40
- */
41
- baseUrl: string
42
- /**
43
- * API key used for SDK authentication.
44
- */
45
- apiKey?: string
46
- /**
47
- * Request timeout in milliseconds.
48
- */
49
- timeoutMs?: number
50
- /**
51
- * Global headers.
52
- */
53
- headers?: Record<string, string>
54
- /**
55
- * Retry policy.
56
- */
57
- retry?: RetryPolicy
58
- }
59
-
60
- /**
61
- * Per-request options.
62
- */
63
- export type RequestOptions = {
64
- method: HttpMethod
65
- path: string
66
- query?: QueryParams
67
- body?: unknown
68
- headers?: Record<string, string>
69
- /**
70
- * Defines whether the request requires an apiKey or is public.
71
- */
72
- auth?: AuthMode
73
- }
74
-
75
- /**
76
- * Base HTTP client for the SDK.
77
- */
78
- export class HttpClient {
79
- private apiKey?: string
80
- private readonly baseUrl: string
81
- private readonly timeoutMs: number
82
- private readonly headers: Record<string, string>
83
- private readonly retry?: RetryPolicy
84
- private healthCheckPromise?: Promise<unknown>
85
-
86
- /**
87
- * Creates an HTTP client with baseUrl and optional apiKey.
88
- */
89
- constructor(options: HttpClientOptions) {
90
- this.baseUrl = options.baseUrl.replace(/\/+$/, '')
91
- this.apiKey = options.apiKey
92
- this.timeoutMs = options.timeoutMs ?? 5000
93
- this.headers = options.headers ?? {}
94
- this.retry = options.retry
95
- }
96
-
97
- /**
98
- * Updates the API key at runtime.
99
- */
100
- setApiKey(apiKey?: string) {
101
- this.apiKey = apiKey
102
- }
103
-
104
- /**
105
- * Checks if an API key is configured.
106
- */
107
- hasApiKey(): boolean {
108
- return !!this.apiKey
109
- }
110
-
111
- startHealthCheck(healthCheck: () => Promise<unknown>) {
112
- if (!this.healthCheckPromise) {
113
- const promise = healthCheck()
114
- this.healthCheckPromise = promise
115
- promise.catch(() => undefined)
116
- }
117
- }
118
-
119
- /**
120
- * GET request.
121
- */
122
- async get<T>(path: string, options?: Omit<RequestOptions, 'method' | 'path'>): Promise<T> {
123
- return this.request<T>({ method: 'GET', path, ...options })
124
- }
125
-
126
- /**
127
- * POST request.
128
- */
129
- async post<T>(path: string, body?: unknown, options?: Omit<RequestOptions, 'method' | 'path' | 'body'>): Promise<T> {
130
- return this.request<T>({ method: 'POST', path, body, ...options })
131
- }
132
-
133
- /**
134
- * PUT request.
135
- */
136
- async put<T>(path: string, body?: unknown, options?: Omit<RequestOptions, 'method' | 'path' | 'body'>): Promise<T> {
137
- return this.request<T>({ method: 'PUT', path, body, ...options })
138
- }
139
-
140
- /**
141
- * PATCH request.
142
- */
143
- async patch<T>(path: string, body?: unknown, options?: Omit<RequestOptions, 'method' | 'path' | 'body'>): Promise<T> {
144
- return this.request<T>({ method: 'PATCH', path, body, ...options })
145
- }
146
-
147
- /**
148
- * DELETE request.
149
- */
150
- async delete<T>(path: string, options?: Omit<RequestOptions, 'method' | 'path'>): Promise<T> {
151
- return this.request<T>({ method: 'DELETE', path, ...options })
152
- }
153
-
154
- /**
155
- * Executes a request with retry logic.
156
- */
157
- async request<T>(options: RequestOptions): Promise<T> {
158
- if (this.healthCheckPromise) {
159
- await this.healthCheckPromise
160
- }
161
- const retry = this.retry ?? { retries: 0 }
162
- const retryOn = retry.retryOn ?? ((response?: Response, error?: unknown) => {
163
- if (response) return response.status === 429 || response.status >= 500
164
- return error instanceof NetworkError || error instanceof TimeoutError || error instanceof TypeError
165
- })
166
- const retryDelayMs = retry.retryDelayMs ?? ((attempt: number) => Math.min(1000 * 2 ** (attempt - 1), 8000))
167
- let lastError: unknown
168
-
169
- for (let attempt = 0; attempt <= retry.retries; attempt += 1) {
170
- try {
171
- const { response, data } = await this.execute(options)
172
- if (!response.ok) {
173
- const retryAfter = this.parseRetryAfter(response.headers.get('retry-after'))
174
- const error = mapHttpError(response.status, data, retryAfter)
175
- if (retryOn(response, error) && attempt < retry.retries) {
176
- await this.delay(retryDelayMs(attempt + 1, response, error))
177
- continue
178
- }
179
- throw error
180
- }
181
- return data as T
182
- } catch (error) {
183
- lastError = error
184
- if (retryOn(undefined, error) && attempt < retry.retries) {
185
- await this.delay(retryDelayMs(attempt + 1, undefined, error))
186
- continue
187
- }
188
- throw error
189
- }
190
- }
191
-
192
- throw lastError
193
- }
194
-
195
- /**
196
- * Executes a request without retry and returns response + data.
197
- */
198
- private async execute(options: RequestOptions): Promise<{ response: Response; data: unknown }> {
199
- const url = this.buildUrl(options.path, options.query)
200
- const headers: Record<string, string> = { ...this.headers, ...options.headers }
201
- if (options.body !== undefined) headers['content-type'] = 'application/json'
202
- const authMode = options.auth ?? 'none'
203
- if (authMode === 'apiKey') {
204
- if (!this.apiKey) throw new AuthError('API key is required')
205
- headers['x-api-key'] = this.apiKey
206
- }
207
-
208
- const controller = new AbortController()
209
- const timer = setTimeout(() => controller.abort(), this.timeoutMs)
210
-
211
- try {
212
- const response = await fetch(url, {
213
- method: options.method,
214
- headers,
215
- body: options.body === undefined ? undefined : JSON.stringify(options.body),
216
- signal: controller.signal,
217
- })
218
- const data = await this.readBody(response)
219
- return { response, data }
220
- } catch (error: any) {
221
- if (error?.name === 'AbortError') {
222
- throw new TimeoutError('Request timeout')
223
- }
224
- if (error instanceof NetworkError || error instanceof TimeoutError) throw error
225
- if (error instanceof Error) {
226
- if (error instanceof AuthError) throw error
227
- }
228
- if (error instanceof Error && 'status' in error) throw error
229
- throw new NetworkError('Network error', { details: error })
230
- } finally {
231
- clearTimeout(timer)
232
- }
233
- }
234
-
235
- /**
236
- * Builds the final URL with query params.
237
- */
238
- private buildUrl(path: string, query?: QueryParams): string {
239
- const base = this.baseUrl
240
- const fullPath = path.startsWith('http') ? path : `${base}${path.startsWith('/') ? '' : '/'}${path}`
241
- if (!query) return fullPath
242
- const params = new URLSearchParams()
243
- for (const [key, value] of Object.entries(query)) {
244
- if (value === undefined) continue
245
- params.set(key, String(value))
246
- }
247
- const suffix = params.toString()
248
- return suffix ? `${fullPath}?${suffix}` : fullPath
249
- }
250
-
251
- /**
252
- * Parses the body as JSON when possible.
253
- */
254
- private async readBody(response: Response): Promise<unknown> {
255
- if (response.status === 204) return undefined
256
- const text = await response.text()
257
- if (!text) return undefined
258
- try {
259
- return JSON.parse(text)
260
- } catch {
261
- return text
262
- }
263
- }
264
-
265
- private parseRetryAfter(value: string | null): number | undefined {
266
- if (!value) return undefined
267
- const seconds = Number(value)
268
- if (!Number.isNaN(seconds) && Number.isFinite(seconds)) {
269
- return Math.max(0, seconds)
270
- }
271
- const parsedDate = Date.parse(value)
272
- if (Number.isNaN(parsedDate)) return undefined
273
- const diffMs = parsedDate - Date.now()
274
- if (diffMs <= 0) return 0
275
- return Math.ceil(diffMs / 1000)
276
- }
277
-
278
- /**
279
- * Simple delay for retries.
280
- */
281
- private async delay(ms: number): Promise<void> {
282
- await new Promise(resolve => setTimeout(resolve, ms))
283
- }
284
- }
package/src/index.ts DELETED
@@ -1,68 +0,0 @@
1
- /**
2
- * Main SDK.
3
- */
4
- export { ObservaSDK } from './sdk'
5
- /**
6
- * SDK options.
7
- */
8
- export type { ObservaSDKOptions } from './sdk'
9
-
10
- /**
11
- * Base HTTP client.
12
- */
13
- export { HttpClient } from './http/httpClient'
14
- /**
15
- * HTTP types.
16
- */
17
- export type { HttpClientOptions, RequestOptions, RetryPolicy, AuthMode } from './http/httpClient'
18
-
19
- /**
20
- * Typed SDK errors.
21
- */
22
- export {
23
- SdkError,
24
- ValidationError,
25
- AuthError,
26
- ForbiddenError,
27
- NotFoundError,
28
- ConflictError,
29
- RateLimitError,
30
- ServerError,
31
- NetworkError,
32
- TimeoutError,
33
- } from './http/errors'
34
-
35
- /**
36
- * Domain APIs.
37
- */
38
- export { UptimeApi } from './apis/uptimeApi'
39
- export { IngestApi } from './apis/ingestApi'
40
- export type { IngestNormalizationOptions } from './apis/ingestApi'
41
-
42
- /**
43
- * Uptime types.
44
- */
45
- export type { UptimeEvent, UptimeStatus, UptimeHeartbeatInput, UptimeSummary } from './domain/uptime'
46
- /**
47
- * Ingestion types.
48
- */
49
- export type {
50
- IngestEvent,
51
- IngestEventContext,
52
- IngestException,
53
- IngestLevel,
54
- IngestRequest,
55
- IngestResponse,
56
- RequestContext,
57
- Stacktrace,
58
- StacktraceFrame,
59
- } from './domain/ingest'
60
- export type {
61
- ProcessContext,
62
- ProcessContextDynamic,
63
- ProcessContextDynamicOptions,
64
- ProcessContextOptions,
65
- ProcessContextStatic,
66
- ProcessContextStaticOptions,
67
- } from './utils/processContext'
68
- export { getProcessContext, getProcessContextDynamic, getProcessContextStatic } from './utils/processContext'
package/src/sdk.ts DELETED
@@ -1,107 +0,0 @@
1
- import { IngestApi, type IngestNormalizationOptions } from './apis/ingestApi'
2
- import { UptimeApi } from './apis/uptimeApi'
3
- import { HttpClient, type RetryPolicy } from './http/httpClient'
4
- import {
5
- getProcessContext,
6
- getProcessContextDynamic,
7
- getProcessContextStatic,
8
- type ProcessContext,
9
- type ProcessContextDynamic,
10
- type ProcessContextDynamicOptions,
11
- type ProcessContextOptions,
12
- type ProcessContextStatic,
13
- type ProcessContextStaticOptions,
14
- } from './utils/processContext'
15
-
16
- /**
17
- * SDK configuration options.
18
- */
19
- export type ObservaSDKOptions = {
20
- /**
21
- * Organization API key used to authenticate SDK requests.
22
- */
23
- apiKey?: string
24
- /**
25
- * Project DSN used to identify the destination of events and heartbeats.
26
- */
27
- dsnKey: string
28
- /**
29
- * Public key for frontend/mobile projects.
30
- */
31
- publicKey?: string
32
- baseUrl?: string
33
- /**
34
- * HTTP request timeout in milliseconds.
35
- */
36
- timeoutMs?: number
37
- /**
38
- * Retry policy for transient errors.
39
- */
40
- retry?: RetryPolicy
41
- /**
42
- * Additional headers sent with every request.
43
- */
44
- headers?: Record<string, string>
45
- ingest?: IngestNormalizationOptions
46
- }
47
-
48
- /**
49
- * Fixed backend target for the SDK.
50
- */
51
- const DEFAULT_BASE_URL = 'https://backend-observa-production.up.railway.app/v1'
52
-
53
- /**
54
- * Main SDK for error ingestion and uptime heartbeats.
55
- */
56
- export class ObservaSDK {
57
- /**
58
- * Uptime API (heartbeats and public reads).
59
- */
60
- readonly uptime: UptimeApi
61
- /**
62
- * Event ingestion API.
63
- */
64
- readonly ingest: IngestApi
65
-
66
- private readonly http: HttpClient
67
-
68
- /**
69
- * Creates an SDK instance with required dsnKey and either apiKey or publicKey.
70
- */
71
- constructor(options: ObservaSDKOptions) {
72
- if (!options || (!options.apiKey && !options.publicKey) || !options.dsnKey) {
73
- throw new Error('ObservaSDK requires dsnKey and either apiKey or publicKey')
74
- }
75
- const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '')
76
- const normalizedBaseUrl = baseUrl.endsWith('/v1') ? baseUrl : `${baseUrl}/v1`
77
- this.http = new HttpClient({
78
- baseUrl: normalizedBaseUrl,
79
- apiKey: options.apiKey,
80
- timeoutMs: options.timeoutMs,
81
- retry: options.retry,
82
- headers: options.headers,
83
- })
84
- this.ingest = new IngestApi(this.http, options.dsnKey, options.ingest, options.publicKey)
85
- this.uptime = new UptimeApi(this.http, options.dsnKey)
86
- this.http.startHealthCheck(() => this.ingest.health(options.dsnKey, options.publicKey))
87
- }
88
-
89
- /**
90
- * Updates the API key used by the SDK at runtime.
91
- */
92
- setApiKey(apiKey: string) {
93
- this.http.setApiKey(apiKey)
94
- }
95
-
96
- getProcessContext(options?: ProcessContextOptions): ProcessContext {
97
- return getProcessContext(options)
98
- }
99
-
100
- getProcessContextStatic(options?: ProcessContextStaticOptions): ProcessContextStatic {
101
- return getProcessContextStatic(options)
102
- }
103
-
104
- getProcessContextDynamic(options?: ProcessContextDynamicOptions): ProcessContextDynamic {
105
- return getProcessContextDynamic(options)
106
- }
107
- }
@@ -1,84 +0,0 @@
1
- export type ProcessContextStatic = {
2
- versions?: NodeJS.ProcessVersions
3
- node?: string
4
- platform?: NodeJS.Platform
5
- arch?: string
6
- releaseName?: string
7
- }
8
-
9
- export type ProcessContextDynamic = {
10
- pid?: number
11
- uptimeSeconds?: number
12
- memory?: NodeJS.MemoryUsage
13
- }
14
-
15
- export type ProcessContext = ProcessContextStatic & ProcessContextDynamic
16
-
17
- export type ProcessContextOptions = {
18
- includeStatic?: boolean
19
- includeDynamic?: boolean
20
- includeVersions?: boolean
21
- includeRuntime?: boolean
22
- includePid?: boolean
23
- includeUptime?: boolean
24
- includeMemory?: boolean
25
- }
26
-
27
- export type ProcessContextStaticOptions = {
28
- includeVersions?: boolean
29
- includeRuntime?: boolean
30
- }
31
-
32
- export type ProcessContextDynamicOptions = {
33
- includePid?: boolean
34
- includeUptime?: boolean
35
- includeMemory?: boolean
36
- }
37
-
38
- export function getProcessContext(options?: ProcessContextOptions): ProcessContext {
39
- const includeStatic = options?.includeStatic ?? true
40
- const includeDynamic = options?.includeDynamic ?? true
41
- const includeVersions = options?.includeVersions ?? true
42
- const includeRuntime = options?.includeRuntime ?? true
43
- const includePid = options?.includePid ?? true
44
- const includeUptime = options?.includeUptime ?? true
45
- const includeMemory = options?.includeMemory ?? true
46
- const context: ProcessContext = {}
47
-
48
- if (includeDynamic) {
49
- if (includePid) context.pid = process.pid
50
- if (includeUptime) context.uptimeSeconds = Math.round(process.uptime())
51
- if (includeMemory) context.memory = process.memoryUsage()
52
- }
53
-
54
- if (includeStatic) {
55
- if (includeVersions) context.versions = process.versions
56
- if (includeRuntime) {
57
- context.node = process.versions.node
58
- context.platform = process.platform
59
- context.arch = process.arch
60
- context.releaseName = process.release?.name
61
- }
62
- }
63
-
64
- return context
65
- }
66
-
67
- export function getProcessContextStatic(options?: ProcessContextStaticOptions): ProcessContextStatic {
68
- return getProcessContext({
69
- includeDynamic: false,
70
- includeStatic: true,
71
- includeVersions: options?.includeVersions,
72
- includeRuntime: options?.includeRuntime,
73
- })
74
- }
75
-
76
- export function getProcessContextDynamic(options?: ProcessContextDynamicOptions): ProcessContextDynamic {
77
- return getProcessContext({
78
- includeStatic: false,
79
- includeDynamic: true,
80
- includePid: options?.includePid,
81
- includeUptime: options?.includeUptime,
82
- includeMemory: options?.includeMemory,
83
- })
84
- }
@@ -1,19 +0,0 @@
1
- import { ValidationError } from '../http/errors'
2
-
3
- /**
4
- * Ensures a string is present and non-empty.
5
- */
6
- export function ensureNonEmpty(value: string, name: string) {
7
- if (!value || value.trim().length === 0) {
8
- throw new ValidationError(`${name} is required`)
9
- }
10
- }
11
-
12
- /**
13
- * Ensures a value is neither null nor undefined.
14
- */
15
- export function ensureDefined<T>(value: T | null | undefined, name: string): asserts value is T {
16
- if (value === null || value === undefined) {
17
- throw new ValidationError(`${name} is required`)
18
- }
19
- }