@edraj/sauron-node 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.
@@ -0,0 +1,309 @@
1
+ /**
2
+ * Sauron wire-contract types (server-side subset).
3
+ *
4
+ * These mirror the LOCKED envelope shape consumed by the Rust ingest gateway
5
+ * (`sauron-core/src/envelope.rs`). Field names, nullability and ordering are
6
+ * load-bearing — do not "clean them up".
7
+ */
8
+ /** Severity level. Matches the backend enum exactly. */
9
+ export type Level = 'debug' | 'info' | 'warning' | 'error' | 'fatal';
10
+ /** A single normalized stack frame (raw, never symbolicated). */
11
+ export interface Frame {
12
+ function: string | null;
13
+ module: string | null;
14
+ filename: string | null;
15
+ abs_path: string | null;
16
+ lineno: number | null;
17
+ colno: number | null;
18
+ in_app: boolean;
19
+ }
20
+ /** How an exception reached the SDK. */
21
+ export interface Mechanism {
22
+ type: string;
23
+ handled: boolean;
24
+ }
25
+ /** The exception payload of an error item. */
26
+ export interface ExceptionValue {
27
+ type: string;
28
+ value: string | null;
29
+ mechanism: Mechanism;
30
+ stacktrace: Frame[];
31
+ }
32
+ /** User attribution attached to an error item. */
33
+ export interface ErrorUser {
34
+ id: string | null;
35
+ email: string | null;
36
+ username: string | null;
37
+ }
38
+ /** Scope user input — a superset of {@link ErrorUser} accepted by `setUser`. */
39
+ export interface User {
40
+ id?: string | null;
41
+ email?: string | null;
42
+ username?: string | null;
43
+ }
44
+ /**
45
+ * A stored breadcrumb, matching `envelope.rs::Breadcrumb`. Attached to captured
46
+ * errors from the active scope's ring buffer.
47
+ */
48
+ export interface Breadcrumb {
49
+ type: string;
50
+ category: string | null;
51
+ message: string | null;
52
+ level: string | null;
53
+ timestamp: string;
54
+ data: Record<string, unknown>;
55
+ }
56
+ /** Caller-supplied breadcrumb; missing fields are defaulted, `timestamp` stamped. */
57
+ export interface BreadcrumbInput {
58
+ type?: string;
59
+ category?: string;
60
+ message?: string;
61
+ level?: Level;
62
+ data?: Record<string, unknown>;
63
+ }
64
+ /** The mutable state carried by a {@link Scope}. */
65
+ export interface ScopeData {
66
+ user: User | null;
67
+ tags: Record<string, string>;
68
+ contexts: Record<string, unknown>;
69
+ extra: Record<string, unknown>;
70
+ breadcrumbs: Breadcrumb[];
71
+ }
72
+ /** An error item (manual `captureException` / `captureMessage`). */
73
+ export interface ErrorItem {
74
+ type: 'error';
75
+ event_id: string;
76
+ level: Level;
77
+ timestamp: string;
78
+ exception: ExceptionValue;
79
+ message: string | null;
80
+ breadcrumbs: Breadcrumb[];
81
+ tags: Record<string, string>;
82
+ contexts?: Record<string, unknown>;
83
+ extra?: Record<string, unknown>;
84
+ fingerprint: string[] | null;
85
+ user: ErrorUser | null;
86
+ session_id: string | null;
87
+ screen: string | null;
88
+ }
89
+ /** A product-analytics event (PostHog-style `track`). */
90
+ export interface EventItem {
91
+ type: 'event';
92
+ name: string;
93
+ distinct_id: string;
94
+ properties: Record<string, unknown>;
95
+ timestamp: string;
96
+ session_id: string | null;
97
+ screen: string | null;
98
+ tags?: Record<string, string>;
99
+ contexts?: Record<string, unknown>;
100
+ extra?: Record<string, unknown>;
101
+ }
102
+ /** An identity association (PostHog-style `identify`). */
103
+ export interface IdentifyItem {
104
+ type: 'identify';
105
+ distinct_id: string;
106
+ anonymous_id: string | null;
107
+ traits: Record<string, unknown>;
108
+ timestamp: string;
109
+ }
110
+ /**
111
+ * A performance transaction — one timed operation. Matches
112
+ * `envelope.rs::TransactionItem`. Optional fields are omitted from the wire
113
+ * JSON when absent (never serialized as `null`).
114
+ */
115
+ export interface TransactionItem {
116
+ type: 'transaction';
117
+ name: string;
118
+ op: string;
119
+ duration_ms: number;
120
+ status?: string;
121
+ http_method?: string;
122
+ http_status?: number;
123
+ url?: string;
124
+ distinct_id?: string;
125
+ timestamp: string;
126
+ }
127
+ /** Caller input for {@link TransactionItem} via `trackTransaction`. */
128
+ export interface TransactionInput {
129
+ name: string;
130
+ /** Operation class: `navigation | http | resource | screen_load | custom`. Default `custom`. */
131
+ op?: string;
132
+ duration_ms: number;
133
+ status?: string;
134
+ http_method?: string;
135
+ http_status?: number;
136
+ url?: string;
137
+ /** Falls back to the scoped user's id when omitted. */
138
+ distinct_id?: string;
139
+ }
140
+ /** Any item that can appear in an envelope's `items` array. */
141
+ export type EnvelopeItem = ErrorItem | EventItem | IdentifyItem | TransactionItem;
142
+ /** A hook run on every outgoing item; return `null` to drop it. */
143
+ export type BeforeSend = (item: EnvelopeItem, hint?: unknown) => EnvelopeItem | null;
144
+ /** A hook run on every breadcrumb; return `null` to drop it. */
145
+ export type BeforeBreadcrumb = (crumb: Breadcrumb, hint?: unknown) => Breadcrumb | null;
146
+ export interface DeviceContext {
147
+ device_id: string;
148
+ }
149
+ export interface OsContext {
150
+ name: string | null;
151
+ version: string | null;
152
+ }
153
+ export interface RuntimeContext {
154
+ name: string | null;
155
+ version: string | null;
156
+ }
157
+ export interface Context {
158
+ device: DeviceContext;
159
+ os: OsContext;
160
+ app: Record<string, unknown>;
161
+ runtime: RuntimeContext;
162
+ user: null;
163
+ }
164
+ export interface SdkInfo {
165
+ name: string;
166
+ version: string;
167
+ }
168
+ export interface EnvelopeHeader {
169
+ dsn: string;
170
+ sdk: SdkInfo;
171
+ sent_at: string;
172
+ environment: string;
173
+ release: string | null;
174
+ }
175
+ /** The complete, serializable envelope posted to the ingest gateway. */
176
+ export interface Envelope {
177
+ header: EnvelopeHeader;
178
+ context: Context;
179
+ items: EnvelopeItem[];
180
+ }
181
+ /** A minimal `Headers`-like view over a response (for reading `Retry-After`). */
182
+ export interface ResponseHeadersLike {
183
+ get(name: string): string | null;
184
+ }
185
+ /** The subset of a `fetch` `Response` the transport inspects. */
186
+ export interface FetchResponse {
187
+ status: number;
188
+ ok?: boolean;
189
+ headers?: ResponseHeadersLike;
190
+ }
191
+ /**
192
+ * A subset of the DOM `fetch` used by the transport. Injectable for tests.
193
+ * The body may be a gzip `Uint8Array`/`Buffer` when compression kicks in.
194
+ */
195
+ export type FetchLike = (url: string, init: {
196
+ method: string;
197
+ headers: Record<string, string>;
198
+ body: string | Uint8Array;
199
+ }) => Promise<FetchResponse>;
200
+ /** Optional deterministic sleep seam (defaults to a real `setTimeout` promise). */
201
+ export type SleepFn = (ms: number) => Promise<void>;
202
+ /**
203
+ * The subset of Node's `process` the opt-in auto-capture / shutdown hooks touch.
204
+ * Injectable so tests can drive the handlers without registering real
205
+ * process-level listeners or terminating the test runner.
206
+ */
207
+ export interface ProcessLike {
208
+ on(event: string, listener: (...args: any[]) => void): unknown;
209
+ removeListener(event: string, listener: (...args: any[]) => void): unknown;
210
+ listeners(event: string): Array<(...args: any[]) => void>;
211
+ exit(code?: number): void;
212
+ }
213
+ /** Transport tuning knobs. */
214
+ export interface TransportOptions {
215
+ /** How often the pending batch is flushed, in ms. Default 5000. */
216
+ flushIntervalMs?: number;
217
+ /** Max items per envelope before an eager flush. Default 30. */
218
+ maxBatch?: number;
219
+ /** Injected HTTP sender. Defaults to global `fetch`. */
220
+ fetchImpl?: FetchLike;
221
+ }
222
+ /** Options accepted by `init`. */
223
+ export interface InitOptions {
224
+ /** `https://<public_key>@<host>/<project_id>` */
225
+ dsn: string;
226
+ environment?: string;
227
+ release?: string | null;
228
+ /** Default tags seeded into the global scope at init. */
229
+ tags?: Record<string, string>;
230
+ /** Default named dev context blocks seeded into the global scope at init. Distinct from the machine `context`. */
231
+ contexts?: Record<string, unknown>;
232
+ /** Default freeform extra values seeded into the global scope at init. */
233
+ extra?: Record<string, unknown>;
234
+ /** Error sample rate in [0, 1]. Default 1 (send everything). */
235
+ sampleRate?: number;
236
+ /** How often the pending batch is flushed, in ms. Default 5000. */
237
+ flushInterval?: number;
238
+ /** Max items per envelope before an eager flush. Default 30. */
239
+ maxBatch?: number;
240
+ /** Breadcrumb ring-buffer size on the global scope. Default 100. */
241
+ maxBreadcrumbs?: number;
242
+ /** Gzip the request body once it exceeds this many bytes. Default 1024. */
243
+ gzipThresholdBytes?: number;
244
+ /** Drop-oldest byte cap for the in-memory send buffer. Default 1 MiB. */
245
+ maxQueueBytes?: number;
246
+ /** Opt-in directory for FIFO disk persistence of pending envelopes. Default off. */
247
+ offlineDir?: string;
248
+ /** Max retries after the first attempt for transient failures. Default 3. */
249
+ maxRetries?: number;
250
+ /**
251
+ * Opt-in: capture uncaught exceptions / unhandled rejections with
252
+ * `mechanism.handled = false`. Default `false`. Never swallows the crash —
253
+ * the process's default exit behavior is preserved after flushing.
254
+ */
255
+ autoCaptureUnhandled?: boolean;
256
+ /**
257
+ * Opt-in: wire `beforeExit`/`SIGTERM`/`SIGINT` to `close()` for a graceful
258
+ * flush on shutdown. Default `false`. Explicit `close()` still works.
259
+ */
260
+ autoShutdown?: boolean;
261
+ /** Runs on every outgoing item just before enqueue; return `null` to drop it. */
262
+ beforeSend?: BeforeSend;
263
+ /** Runs on every breadcrumb before it is stored; return `null` to drop it. */
264
+ beforeBreadcrumb?: BeforeBreadcrumb;
265
+ /** Injected HTTP sender (for tests). Defaults to global `fetch`. */
266
+ fetchImpl?: FetchLike;
267
+ debug?: boolean;
268
+ }
269
+ /** Fully-resolved options with all defaults applied. */
270
+ export interface ResolvedOptions {
271
+ dsn: string;
272
+ environment: string;
273
+ release: string | null;
274
+ tags: Record<string, string>;
275
+ contexts: Record<string, unknown>;
276
+ extra: Record<string, unknown>;
277
+ sampleRate: number;
278
+ flushInterval: number;
279
+ maxBatch: number;
280
+ maxBreadcrumbs: number;
281
+ gzipThresholdBytes: number;
282
+ maxQueueBytes: number;
283
+ offlineDir: string | null;
284
+ maxRetries: number;
285
+ autoCaptureUnhandled: boolean;
286
+ autoShutdown: boolean;
287
+ beforeSend?: BeforeSend;
288
+ beforeBreadcrumb?: BeforeBreadcrumb;
289
+ fetchImpl?: FetchLike;
290
+ debug: boolean;
291
+ }
292
+ /**
293
+ * Per-capture metadata overrides shared by `captureMessage` and `track`, and the
294
+ * metadata subset of {@link CaptureExceptionOptions}. Empty maps are omitted on
295
+ * the wire per the emit convention.
296
+ */
297
+ export interface MetadataOptions {
298
+ tags?: Record<string, string>;
299
+ contexts?: Record<string, unknown>;
300
+ extra?: Record<string, unknown>;
301
+ }
302
+ /** Extra attribution for `captureException`. */
303
+ export interface CaptureExceptionOptions extends MetadataOptions {
304
+ user?: Partial<ErrorUser> | null;
305
+ level?: Level;
306
+ handled?: boolean;
307
+ /** Client-supplied fingerprint override (honored verbatim by the backend). */
308
+ fingerprint?: string[] | null;
309
+ }
package/dist/types.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Sauron wire-contract types (server-side subset).
3
+ *
4
+ * These mirror the LOCKED envelope shape consumed by the Rust ingest gateway
5
+ * (`sauron-core/src/envelope.rs`). Field names, nullability and ordering are
6
+ * load-bearing — do not "clean them up".
7
+ */
8
+ export {};
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@edraj/sauron-node",
3
+ "version": "1.0.0",
4
+ "description": "Sauron server-side Node/TypeScript SDK: product-analytics events + exception capture for Node backends.",
5
+ "homepage": "https://github.com/edraj/sauron/tree/main/sdks/node#readme",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/edraj/sauron.git",
9
+ "directory": "sdks/node"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/edraj/sauron/issues"
13
+ },
14
+ "type": "module",
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js"
21
+ },
22
+ "./package.json": "./package.json"
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "README.md",
27
+ "CHANGELOG.md",
28
+ "LICENSE"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsc -p tsconfig.build.json",
32
+ "test": "vitest run",
33
+ "test:watch": "vitest",
34
+ "typecheck": "tsc --noEmit",
35
+ "prepublishOnly": "npm run typecheck && npm run test && npm run build"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^20.19.43",
39
+ "typescript": "^5.9.0",
40
+ "vitest": "^3.2.0"
41
+ },
42
+ "engines": {
43
+ "node": ">=18"
44
+ },
45
+ "keywords": [
46
+ "sauron",
47
+ "error-tracking",
48
+ "analytics",
49
+ "observability",
50
+ "monitoring",
51
+ "sdk",
52
+ "node",
53
+ "server"
54
+ ],
55
+ "license": "AGPL-3.0-only",
56
+ "publishConfig": {
57
+ "access": "public"
58
+ }
59
+ }