@observa/sdk 2.2.1 → 2.3.1

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 (69) hide show
  1. package/README.md +12 -0
  2. package/dist/apis/ingestApi.d.ts +12 -1
  3. package/dist/apis/ingestApi.d.ts.map +1 -1
  4. package/dist/apis/ingestApi.js +136 -3
  5. package/dist/apis/ingestApi.js.map +1 -1
  6. package/dist/domain/ingest.d.ts +15 -1
  7. package/dist/domain/ingest.d.ts.map +1 -1
  8. package/dist/index.d.ts +494 -20
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +568 -18
  11. package/dist/index.js.map +1 -1
  12. package/dist/sdk.d.ts +2 -1
  13. package/dist/sdk.d.ts.map +1 -1
  14. package/dist/sdk.js +1 -1
  15. package/dist/sdk.js.map +1 -1
  16. package/dist/src/apis/ingestApi.d.ts +22 -0
  17. package/dist/src/apis/ingestApi.js +167 -0
  18. package/dist/src/apis/ingestApi.js.map +1 -0
  19. package/dist/src/apis/uptimeApi.d.ts +11 -0
  20. package/dist/src/apis/uptimeApi.js +39 -0
  21. package/dist/src/apis/uptimeApi.js.map +1 -0
  22. package/dist/src/domain/ingest.d.ts +47 -0
  23. package/dist/src/domain/ingest.js +2 -0
  24. package/dist/src/domain/ingest.js.map +1 -0
  25. package/dist/src/domain/uptime.d.ts +23 -0
  26. package/dist/src/domain/uptime.js +2 -0
  27. package/dist/src/domain/uptime.js.map +1 -0
  28. package/dist/src/http/errors.d.ts +33 -0
  29. package/dist/src/http/errors.js +54 -0
  30. package/dist/src/http/errors.js.map +1 -0
  31. package/dist/src/http/httpClient.d.ts +45 -0
  32. package/dist/src/http/httpClient.js +165 -0
  33. package/dist/src/http/httpClient.js.map +1 -0
  34. package/dist/src/index.d.ts +12 -0
  35. package/dist/src/index.js +7 -0
  36. package/dist/src/index.js.map +1 -0
  37. package/dist/src/sdk.d.ts +23 -0
  38. package/dist/src/sdk.js +40 -0
  39. package/dist/src/sdk.js.map +1 -0
  40. package/dist/src/utils/processContext.d.ts +34 -0
  41. package/dist/src/utils/processContext.js +47 -0
  42. package/dist/src/utils/processContext.js.map +1 -0
  43. package/dist/src/utils/validate.d.ts +2 -0
  44. package/dist/src/utils/validate.js +12 -0
  45. package/dist/src/utils/validate.js.map +1 -0
  46. package/dist/tests/httpClient.test.d.ts +1 -0
  47. package/dist/tests/httpClient.test.js +47 -0
  48. package/dist/tests/httpClient.test.js.map +1 -0
  49. package/dist/tests/ingestApi.test.d.ts +1 -0
  50. package/dist/tests/ingestApi.test.js +65 -0
  51. package/dist/tests/ingestApi.test.js.map +1 -0
  52. package/dist/tests/observaSdk.integration.test.d.ts +1 -0
  53. package/dist/tests/observaSdk.integration.test.js +51 -0
  54. package/dist/tests/observaSdk.integration.test.js.map +1 -0
  55. package/dist/tests/sdk.test.d.ts +1 -0
  56. package/dist/tests/sdk.test.js +104 -0
  57. package/dist/tests/sdk.test.js.map +1 -0
  58. package/dist/tsconfig.build.tsbuildinfo +1 -0
  59. package/package.json +11 -3
  60. package/src/apis/ingestApi.ts +185 -0
  61. package/src/apis/uptimeApi.ts +58 -0
  62. package/src/domain/ingest.ts +89 -0
  63. package/src/domain/uptime.ts +86 -0
  64. package/src/http/errors.ts +88 -0
  65. package/src/http/httpClient.ts +277 -0
  66. package/src/index.ts +68 -0
  67. package/src/sdk.ts +103 -0
  68. package/src/utils/processContext.ts +84 -0
  69. package/src/utils/validate.ts +19 -0
@@ -0,0 +1,89 @@
1
+ import type { ProcessContextDynamic, ProcessContextStatic } from '../utils/processContext'
2
+
3
+ /**
4
+ * Severity levels for events.
5
+ */
6
+ export type IngestLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal'
7
+
8
+ export type StacktraceFrame = {
9
+ filename?: string
10
+ function?: string
11
+ lineno?: number
12
+ colno?: number
13
+ in_app?: boolean
14
+ }
15
+
16
+ export type Stacktrace = {
17
+ frames: StacktraceFrame[]
18
+ }
19
+
20
+ export type IngestException = {
21
+ type: string
22
+ value: string
23
+ stacktrace?: Stacktrace
24
+ }
25
+
26
+ export type RequestContext = {
27
+ requestId?: string
28
+ userId?: string
29
+ [key: string]: unknown
30
+ }
31
+
32
+ export type IngestEventContext = {
33
+ system?: ProcessContextDynamic
34
+ runtime?: ProcessContextStatic
35
+ request?: RequestContext
36
+ }
37
+
38
+ /**
39
+ * Ingestion event.
40
+ */
41
+ export type IngestEvent = {
42
+ /**
43
+ * Unique event identifier.
44
+ */
45
+ event_id?: string
46
+ /**
47
+ * Event ISO timestamp.
48
+ */
49
+ timestamp?: string
50
+ schema_version?: number
51
+ /**
52
+ * Severity level.
53
+ */
54
+ level?: IngestLevel
55
+ /**
56
+ * Main message.
57
+ */
58
+ message?: string
59
+ exception?: IngestException
60
+ context?: IngestEventContext
61
+ tags?: Record<string, string>
62
+ extra?: Record<string, unknown>
63
+ }
64
+
65
+ /**
66
+ * Payload for sending an event to the backend.
67
+ */
68
+ export type IngestRequest = {
69
+ /**
70
+ * Project DSN. Uses the SDK default when omitted.
71
+ */
72
+ dsnKey?: string
73
+ /**
74
+ * Event to record.
75
+ */
76
+ event: IngestEvent
77
+ idempotencyKey?: string
78
+ sdkVersion?: string
79
+ }
80
+
81
+ /**
82
+ * Response after sending an event.
83
+ */
84
+ export type IngestResponse = {
85
+ /**
86
+ * Assigned event identifier.
87
+ */
88
+ event_id: string
89
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Possible uptime statuses.
3
+ */
4
+ export type UptimeStatus = 'up' | 'down' | 'degraded'
5
+
6
+ /**
7
+ * Recorded uptime event.
8
+ */
9
+ export type UptimeEvent = {
10
+ /**
11
+ * Event identifier.
12
+ */
13
+ id: string
14
+ /**
15
+ * Project the event belongs to.
16
+ */
17
+ projectId: string
18
+ /**
19
+ * Uptime status.
20
+ */
21
+ status: UptimeStatus
22
+ /**
23
+ * Optional message for the event.
24
+ */
25
+ message?: string
26
+ /**
27
+ * Response time in milliseconds.
28
+ */
29
+ responseTimeMs?: number
30
+ /**
31
+ * ISO timestamp of the check.
32
+ */
33
+ checkedAt?: string
34
+ /**
35
+ * ISO timestamp of backend creation.
36
+ */
37
+ createdAt?: string
38
+ }
39
+
40
+ /**
41
+ * Payload for sending an uptime heartbeat.
42
+ */
43
+ export type UptimeHeartbeatInput = {
44
+ /**
45
+ * Project DSN. Uses the SDK default when omitted.
46
+ */
47
+ dsnKey?: string
48
+ /**
49
+ * Reported status.
50
+ */
51
+ status: UptimeStatus
52
+ /**
53
+ * Response time in milliseconds.
54
+ */
55
+ responseTimeMs?: number
56
+ /**
57
+ * ISO timestamp of the check.
58
+ */
59
+ checkedAt?: string
60
+ /**
61
+ * Optional message.
62
+ */
63
+ message?: string
64
+ }
65
+
66
+ /**
67
+ * Daily uptime summary.
68
+ */
69
+ export type UptimeSummary = {
70
+ /**
71
+ * ISO date of the summary.
72
+ */
73
+ date: string
74
+ /**
75
+ * Hours without data.
76
+ */
77
+ missingHours?: number
78
+ /**
79
+ * Accumulated delay minutes.
80
+ */
81
+ delayMinutes?: number
82
+ /**
83
+ * Number of delayed events.
84
+ */
85
+ delayedCount?: number
86
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Additional data to enrich SDK errors.
3
+ */
4
+ export type ErrorDetails = {
5
+ status?: number
6
+ code?: string
7
+ details?: unknown
8
+ retryAfter?: number
9
+ }
10
+
11
+ /**
12
+ * Base SDK error.
13
+ */
14
+ export class SdkError extends Error {
15
+ readonly status?: number
16
+ readonly code?: string
17
+ readonly details?: unknown
18
+
19
+ /**
20
+ * Creates an error with optional metadata.
21
+ */
22
+ constructor(message: string, details?: ErrorDetails) {
23
+ super(message)
24
+ this.name = new.target.name
25
+ this.status = details?.status
26
+ this.code = details?.code
27
+ this.details = details?.details
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Input validation error.
33
+ */
34
+ export class ValidationError extends SdkError { }
35
+ /**
36
+ * Authentication error.
37
+ */
38
+ export class AuthError extends SdkError { }
39
+ /**
40
+ * Error caused by insufficient permissions.
41
+ */
42
+ export class ForbiddenError extends SdkError { }
43
+ /**
44
+ * Error when a resource does not exist.
45
+ */
46
+ export class NotFoundError extends SdkError { }
47
+ /**
48
+ * Error caused by a state conflict.
49
+ */
50
+ export class ConflictError extends SdkError { }
51
+ /**
52
+ * Error caused by rate limiting.
53
+ */
54
+ export class RateLimitError extends SdkError {
55
+ readonly retryAfter?: number
56
+
57
+ constructor(message: string, details?: ErrorDetails) {
58
+ super(message, details)
59
+ this.retryAfter = details?.retryAfter
60
+ }
61
+ }
62
+ /**
63
+ * Server-side error.
64
+ */
65
+ export class ServerError extends SdkError { }
66
+ /**
67
+ * Network error.
68
+ */
69
+ export class NetworkError extends SdkError { }
70
+ /**
71
+ * Request timeout error.
72
+ */
73
+ export class TimeoutError extends SdkError { }
74
+
75
+ /**
76
+ * Maps HTTP status codes to typed SDK errors.
77
+ */
78
+ export function mapHttpError(status: number, details?: unknown, retryAfter?: number): SdkError {
79
+ const info = { status, details, retryAfter }
80
+ if (status === 400) return new ValidationError('Invalid request', info)
81
+ if (status === 401) return new AuthError('Unauthorized', info)
82
+ if (status === 403) return new ForbiddenError('Forbidden', info)
83
+ if (status === 404) return new NotFoundError('Resource not found', info)
84
+ if (status === 409) return new ConflictError('Conflict', info)
85
+ if (status === 429) return new RateLimitError('Rate limit exceeded', info)
86
+ if (status >= 500) return new ServerError('Server error', info)
87
+ return new SdkError('HTTP error', info)
88
+ }
@@ -0,0 +1,277 @@
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
+ startHealthCheck(healthCheck: () => Promise<unknown>) {
105
+ if (!this.healthCheckPromise) {
106
+ const promise = healthCheck()
107
+ this.healthCheckPromise = promise
108
+ promise.catch(() => undefined)
109
+ }
110
+ }
111
+
112
+ /**
113
+ * GET request.
114
+ */
115
+ async get<T>(path: string, options?: Omit<RequestOptions, 'method' | 'path'>): Promise<T> {
116
+ return this.request<T>({ method: 'GET', path, ...options })
117
+ }
118
+
119
+ /**
120
+ * POST request.
121
+ */
122
+ async post<T>(path: string, body?: unknown, options?: Omit<RequestOptions, 'method' | 'path' | 'body'>): Promise<T> {
123
+ return this.request<T>({ method: 'POST', path, body, ...options })
124
+ }
125
+
126
+ /**
127
+ * PUT request.
128
+ */
129
+ async put<T>(path: string, body?: unknown, options?: Omit<RequestOptions, 'method' | 'path' | 'body'>): Promise<T> {
130
+ return this.request<T>({ method: 'PUT', path, body, ...options })
131
+ }
132
+
133
+ /**
134
+ * PATCH request.
135
+ */
136
+ async patch<T>(path: string, body?: unknown, options?: Omit<RequestOptions, 'method' | 'path' | 'body'>): Promise<T> {
137
+ return this.request<T>({ method: 'PATCH', path, body, ...options })
138
+ }
139
+
140
+ /**
141
+ * DELETE request.
142
+ */
143
+ async delete<T>(path: string, options?: Omit<RequestOptions, 'method' | 'path'>): Promise<T> {
144
+ return this.request<T>({ method: 'DELETE', path, ...options })
145
+ }
146
+
147
+ /**
148
+ * Executes a request with retry logic.
149
+ */
150
+ async request<T>(options: RequestOptions): Promise<T> {
151
+ if (this.healthCheckPromise) {
152
+ await this.healthCheckPromise
153
+ }
154
+ const retry = this.retry ?? { retries: 0 }
155
+ const retryOn = retry.retryOn ?? ((response?: Response, error?: unknown) => {
156
+ if (response) return response.status === 429 || response.status >= 500
157
+ return error instanceof NetworkError || error instanceof TimeoutError || error instanceof TypeError
158
+ })
159
+ const retryDelayMs = retry.retryDelayMs ?? ((attempt: number) => Math.min(1000 * 2 ** (attempt - 1), 8000))
160
+ let lastError: unknown
161
+
162
+ for (let attempt = 0; attempt <= retry.retries; attempt += 1) {
163
+ try {
164
+ const { response, data } = await this.execute(options)
165
+ if (!response.ok) {
166
+ const retryAfter = this.parseRetryAfter(response.headers.get('retry-after'))
167
+ const error = mapHttpError(response.status, data, retryAfter)
168
+ if (retryOn(response, error) && attempt < retry.retries) {
169
+ await this.delay(retryDelayMs(attempt + 1, response, error))
170
+ continue
171
+ }
172
+ throw error
173
+ }
174
+ return data as T
175
+ } catch (error) {
176
+ lastError = error
177
+ if (retryOn(undefined, error) && attempt < retry.retries) {
178
+ await this.delay(retryDelayMs(attempt + 1, undefined, error))
179
+ continue
180
+ }
181
+ throw error
182
+ }
183
+ }
184
+
185
+ throw lastError
186
+ }
187
+
188
+ /**
189
+ * Executes a request without retry and returns response + data.
190
+ */
191
+ private async execute(options: RequestOptions): Promise<{ response: Response; data: unknown }> {
192
+ const url = this.buildUrl(options.path, options.query)
193
+ const headers: Record<string, string> = { ...this.headers, ...options.headers }
194
+ if (options.body !== undefined) headers['content-type'] = 'application/json'
195
+ const authMode = options.auth ?? 'none'
196
+ if (authMode === 'apiKey') {
197
+ if (!this.apiKey) throw new AuthError('API key is required')
198
+ headers['x-api-key'] = this.apiKey
199
+ }
200
+
201
+ const controller = new AbortController()
202
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs)
203
+
204
+ try {
205
+ const response = await fetch(url, {
206
+ method: options.method,
207
+ headers,
208
+ body: options.body === undefined ? undefined : JSON.stringify(options.body),
209
+ signal: controller.signal,
210
+ })
211
+ const data = await this.readBody(response)
212
+ return { response, data }
213
+ } catch (error: any) {
214
+ if (error?.name === 'AbortError') {
215
+ throw new TimeoutError('Request timeout')
216
+ }
217
+ if (error instanceof NetworkError || error instanceof TimeoutError) throw error
218
+ if (error instanceof Error) {
219
+ if (error instanceof AuthError) throw error
220
+ }
221
+ if (error instanceof Error && 'status' in error) throw error
222
+ throw new NetworkError('Network error', { details: error })
223
+ } finally {
224
+ clearTimeout(timer)
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Builds the final URL with query params.
230
+ */
231
+ private buildUrl(path: string, query?: QueryParams): string {
232
+ const base = this.baseUrl
233
+ const fullPath = path.startsWith('http') ? path : `${base}${path.startsWith('/') ? '' : '/'}${path}`
234
+ if (!query) return fullPath
235
+ const params = new URLSearchParams()
236
+ for (const [key, value] of Object.entries(query)) {
237
+ if (value === undefined) continue
238
+ params.set(key, String(value))
239
+ }
240
+ const suffix = params.toString()
241
+ return suffix ? `${fullPath}?${suffix}` : fullPath
242
+ }
243
+
244
+ /**
245
+ * Parses the body as JSON when possible.
246
+ */
247
+ private async readBody(response: Response): Promise<unknown> {
248
+ if (response.status === 204) return undefined
249
+ const text = await response.text()
250
+ if (!text) return undefined
251
+ try {
252
+ return JSON.parse(text)
253
+ } catch {
254
+ return text
255
+ }
256
+ }
257
+
258
+ private parseRetryAfter(value: string | null): number | undefined {
259
+ if (!value) return undefined
260
+ const seconds = Number(value)
261
+ if (!Number.isNaN(seconds) && Number.isFinite(seconds)) {
262
+ return Math.max(0, seconds)
263
+ }
264
+ const parsedDate = Date.parse(value)
265
+ if (Number.isNaN(parsedDate)) return undefined
266
+ const diffMs = parsedDate - Date.now()
267
+ if (diffMs <= 0) return 0
268
+ return Math.ceil(diffMs / 1000)
269
+ }
270
+
271
+ /**
272
+ * Simple delay for retries.
273
+ */
274
+ private async delay(ms: number): Promise<void> {
275
+ await new Promise(resolve => setTimeout(resolve, ms))
276
+ }
277
+ }
package/src/index.ts ADDED
@@ -0,0 +1,68 @@
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 ADDED
@@ -0,0 +1,103 @@
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
+ baseUrl?: string
29
+ /**
30
+ * HTTP request timeout in milliseconds.
31
+ */
32
+ timeoutMs?: number
33
+ /**
34
+ * Retry policy for transient errors.
35
+ */
36
+ retry?: RetryPolicy
37
+ /**
38
+ * Additional headers sent with every request.
39
+ */
40
+ headers?: Record<string, string>
41
+ ingest?: IngestNormalizationOptions
42
+ }
43
+
44
+ /**
45
+ * Fixed backend target for the SDK.
46
+ */
47
+ const DEFAULT_BASE_URL = 'https://backend-observa-production.up.railway.app/v1'
48
+
49
+ /**
50
+ * Main SDK for error ingestion and uptime heartbeats.
51
+ */
52
+ export class ObservaSDK {
53
+ /**
54
+ * Uptime API (heartbeats and public reads).
55
+ */
56
+ readonly uptime: UptimeApi
57
+ /**
58
+ * Event ingestion API.
59
+ */
60
+ readonly ingest: IngestApi
61
+
62
+ private readonly http: HttpClient
63
+
64
+ /**
65
+ * Creates an SDK instance with required apiKey and dsnKey.
66
+ */
67
+ constructor(options: ObservaSDKOptions) {
68
+ if (!options || !options.apiKey || !options.dsnKey) {
69
+ throw new Error('ObservaSDK requires both apiKey and dsnKey')
70
+ }
71
+ const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '')
72
+ const normalizedBaseUrl = baseUrl.endsWith('/v1') ? baseUrl : `${baseUrl}/v1`
73
+ this.http = new HttpClient({
74
+ baseUrl: normalizedBaseUrl,
75
+ apiKey: options.apiKey,
76
+ timeoutMs: options.timeoutMs,
77
+ retry: options.retry,
78
+ headers: options.headers,
79
+ })
80
+ this.ingest = new IngestApi(this.http, options.dsnKey, options.ingest)
81
+ this.uptime = new UptimeApi(this.http, options.dsnKey)
82
+ this.http.startHealthCheck(() => this.ingest.health(options.dsnKey))
83
+ }
84
+
85
+ /**
86
+ * Updates the API key used by the SDK at runtime.
87
+ */
88
+ setApiKey(apiKey: string) {
89
+ this.http.setApiKey(apiKey)
90
+ }
91
+
92
+ getProcessContext(options?: ProcessContextOptions): ProcessContext {
93
+ return getProcessContext(options)
94
+ }
95
+
96
+ getProcessContextStatic(options?: ProcessContextStaticOptions): ProcessContextStatic {
97
+ return getProcessContextStatic(options)
98
+ }
99
+
100
+ getProcessContextDynamic(options?: ProcessContextDynamicOptions): ProcessContextDynamic {
101
+ return getProcessContextDynamic(options)
102
+ }
103
+ }