@gunsole/core 0.1.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.
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/dist/index.cjs +477 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +227 -0
- package/dist/index.d.ts +227 -0
- package/dist/index.js +474 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 push1kb
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# @gunsole/core
|
|
2
|
+
|
|
3
|
+
Gunsole JavaScript/TypeScript SDK for browser and Node.js environments.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @gunsole/core
|
|
9
|
+
# or
|
|
10
|
+
npm install @gunsole/core
|
|
11
|
+
# or
|
|
12
|
+
yarn add @gunsole/core
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Basic Setup
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { createGunsoleClient } from "@gunsole/core";
|
|
21
|
+
|
|
22
|
+
const gunsole = createGunsoleClient({
|
|
23
|
+
projectId: "your-project-id",
|
|
24
|
+
apiKey: "your-api-key",
|
|
25
|
+
mode: "cloud", // or "desktop" | "local"
|
|
26
|
+
env: "production",
|
|
27
|
+
appName: "my-app",
|
|
28
|
+
appVersion: "1.0.0",
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Logging
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// Simple log
|
|
36
|
+
gunsole.log({
|
|
37
|
+
level: "info",
|
|
38
|
+
bucket: "user_action",
|
|
39
|
+
message: "User clicked button",
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Log with context and tags
|
|
43
|
+
gunsole.log({
|
|
44
|
+
level: "error",
|
|
45
|
+
bucket: "api_error",
|
|
46
|
+
message: "Failed to fetch user data",
|
|
47
|
+
context: {
|
|
48
|
+
userId: "123",
|
|
49
|
+
endpoint: "/api/users",
|
|
50
|
+
statusCode: 500,
|
|
51
|
+
},
|
|
52
|
+
tags: {
|
|
53
|
+
feature: "user-management",
|
|
54
|
+
severity: "high",
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### User Tracking
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
gunsole.setUser({
|
|
63
|
+
id: "user-123",
|
|
64
|
+
email: "user@example.com",
|
|
65
|
+
name: "John Doe",
|
|
66
|
+
traits: {
|
|
67
|
+
plan: "premium",
|
|
68
|
+
signupDate: "2024-01-01",
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Session Tracking
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
gunsole.setSessionId("session-abc-123");
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Global Error Handlers
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// Attach automatic error tracking
|
|
83
|
+
gunsole.attachGlobalErrorHandlers();
|
|
84
|
+
|
|
85
|
+
// Detach when done
|
|
86
|
+
gunsole.detachGlobalErrorHandlers();
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Manual Flush
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// Flush pending logs immediately
|
|
93
|
+
await gunsole.flush();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Configuration
|
|
97
|
+
|
|
98
|
+
### Modes
|
|
99
|
+
|
|
100
|
+
- `cloud`: Sends logs to `https://api.gunsole.com` (default for SaaS)
|
|
101
|
+
- `desktop`: Sends logs to `http://localhost:8787` (Gunsole Desktop app)
|
|
102
|
+
- `local`: Sends logs to `http://localhost:17655` (local development)
|
|
103
|
+
|
|
104
|
+
### Options
|
|
105
|
+
|
|
106
|
+
- `projectId` (required): Your Gunsole project identifier
|
|
107
|
+
- `apiKey` (optional): Your API key (required for cloud mode)
|
|
108
|
+
- `mode` (required): Client mode (`"desktop" | "local" | "cloud"`)
|
|
109
|
+
- `endpoint` (optional): Custom endpoint URL (overrides mode default)
|
|
110
|
+
- `env` (optional): Environment name (e.g., "production", "staging")
|
|
111
|
+
- `appName` (optional): Application name
|
|
112
|
+
- `appVersion` (optional): Application version
|
|
113
|
+
- `defaultTags` (optional): Default tags applied to all logs
|
|
114
|
+
- `batchSize` (optional): Number of logs to batch before sending (default: 10)
|
|
115
|
+
- `flushInterval` (optional): Auto-flush interval in ms (default: 5000)
|
|
116
|
+
|
|
117
|
+
### Cleanup
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// Flush remaining logs and release resources
|
|
121
|
+
await gunsole.destroy();
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
> **Node.js note:** The `attachGlobalErrorHandlers()` method registers an `uncaughtException` listener. Be aware that in Node.js, uncaught exceptions leave the process in an undefined state. The SDK captures the error for logging but does not re-throw or exit — you may want to add your own handler that calls `process.exit(1)` after flushing.
|
|
125
|
+
|
|
126
|
+
## API Reference
|
|
127
|
+
|
|
128
|
+
See [SDK Reference](../../docs/sdk-reference.md) for full API documentation including typed buckets, tag schemas, and all configuration options.
|
|
129
|
+
|
|
130
|
+
## Features
|
|
131
|
+
|
|
132
|
+
- ✅ Browser and Node.js support
|
|
133
|
+
- ✅ Automatic batching and flushing
|
|
134
|
+
- ✅ Retry logic with exponential backoff
|
|
135
|
+
- ✅ Never crashes the host application
|
|
136
|
+
- ✅ TypeScript support with full type definitions
|
|
137
|
+
- ✅ Tree-shakeable ESM and CJS builds
|
|
138
|
+
- ✅ Global error handler integration
|
|
139
|
+
|
|
140
|
+
## Development
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Install dependencies
|
|
144
|
+
pnpm install
|
|
145
|
+
|
|
146
|
+
# Build
|
|
147
|
+
pnpm build
|
|
148
|
+
|
|
149
|
+
# Test
|
|
150
|
+
pnpm test
|
|
151
|
+
|
|
152
|
+
# Lint
|
|
153
|
+
pnpm lint
|
|
154
|
+
|
|
155
|
+
# Type check
|
|
156
|
+
pnpm typecheck
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT
|
|
162
|
+
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/buckets.ts
|
|
4
|
+
var RESERVED_NAMES = /* @__PURE__ */ new Set([
|
|
5
|
+
"log",
|
|
6
|
+
"info",
|
|
7
|
+
"debug",
|
|
8
|
+
"warn",
|
|
9
|
+
"error",
|
|
10
|
+
"setUser",
|
|
11
|
+
"setSessionId",
|
|
12
|
+
"flush",
|
|
13
|
+
"destroy",
|
|
14
|
+
"attachGlobalErrorHandlers",
|
|
15
|
+
"detachGlobalErrorHandlers"
|
|
16
|
+
]);
|
|
17
|
+
function createBucketLogger(client, bucketName) {
|
|
18
|
+
const logAtLevel = (level, message, options) => {
|
|
19
|
+
client.log(level, {
|
|
20
|
+
...options,
|
|
21
|
+
message,
|
|
22
|
+
bucket: bucketName
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
const logger = ((message, options) => {
|
|
26
|
+
logAtLevel("info", message, options);
|
|
27
|
+
});
|
|
28
|
+
logger.info = (message, options) => logAtLevel("info", message, options);
|
|
29
|
+
logger.debug = (message, options) => logAtLevel("debug", message, options);
|
|
30
|
+
logger.warn = (message, options) => logAtLevel("warn", message, options);
|
|
31
|
+
logger.error = (message, options) => logAtLevel("error", message, options);
|
|
32
|
+
return logger;
|
|
33
|
+
}
|
|
34
|
+
function attachBuckets(client, buckets) {
|
|
35
|
+
for (const name of buckets) {
|
|
36
|
+
if (RESERVED_NAMES.has(name)) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Bucket name "${name}" conflicts with a reserved GunsoleClient method`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
client[name] = createBucketLogger(client, name);
|
|
42
|
+
}
|
|
43
|
+
return client;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/config.ts
|
|
47
|
+
var DEFAULT_ENDPOINTS = {
|
|
48
|
+
desktop: "http://localhost:8787",
|
|
49
|
+
local: "http://localhost:17655",
|
|
50
|
+
cloud: "https://api.gunsole.com"
|
|
51
|
+
};
|
|
52
|
+
var DEFAULT_CONFIG = {
|
|
53
|
+
batchSize: 10,
|
|
54
|
+
flushInterval: 5e3
|
|
55
|
+
};
|
|
56
|
+
function resolveEndpoint(mode, customEndpoint) {
|
|
57
|
+
if (customEndpoint) {
|
|
58
|
+
return customEndpoint;
|
|
59
|
+
}
|
|
60
|
+
return DEFAULT_ENDPOINTS[mode];
|
|
61
|
+
}
|
|
62
|
+
function normalizeConfig(config) {
|
|
63
|
+
if (!config.projectId) {
|
|
64
|
+
throw new Error("projectId is required");
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
projectId: config.projectId,
|
|
68
|
+
apiKey: config.apiKey ?? "",
|
|
69
|
+
mode: config.mode,
|
|
70
|
+
endpoint: resolveEndpoint(config.mode, config.endpoint),
|
|
71
|
+
env: config.env ?? "",
|
|
72
|
+
appName: config.appName ?? "",
|
|
73
|
+
appVersion: config.appVersion ?? "",
|
|
74
|
+
defaultTags: config.defaultTags ?? {},
|
|
75
|
+
batchSize: config.batchSize ?? DEFAULT_CONFIG.batchSize,
|
|
76
|
+
flushInterval: config.flushInterval ?? DEFAULT_CONFIG.flushInterval,
|
|
77
|
+
fetch: config.fetch,
|
|
78
|
+
isDebug: config.isDebug ?? false,
|
|
79
|
+
isDisabled: config.isDisabled ?? false,
|
|
80
|
+
buckets: config.buckets ?? []
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/utils/env.ts
|
|
85
|
+
function isBrowser() {
|
|
86
|
+
return typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
87
|
+
}
|
|
88
|
+
function isNode() {
|
|
89
|
+
return typeof process !== "undefined" && typeof process.versions !== "undefined" && typeof process.versions.node !== "undefined";
|
|
90
|
+
}
|
|
91
|
+
function getFetch(customFetch) {
|
|
92
|
+
if (isBrowser()) {
|
|
93
|
+
return window.fetch.bind(window);
|
|
94
|
+
}
|
|
95
|
+
if (isNode()) {
|
|
96
|
+
if (typeof globalThis.fetch !== "undefined") {
|
|
97
|
+
return globalThis.fetch;
|
|
98
|
+
}
|
|
99
|
+
throw new Error(
|
|
100
|
+
"fetch is not available. Please use Node.js 18+ or provide a custom fetch implementation in the config"
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
throw new Error("Unsupported environment: neither browser nor Node.js");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/transport.ts
|
|
107
|
+
var MAX_RETRIES = 3;
|
|
108
|
+
var BASE_DELAY_MS = 1e3;
|
|
109
|
+
function calculateBackoffDelay(attempt) {
|
|
110
|
+
return BASE_DELAY_MS * 2 ** attempt;
|
|
111
|
+
}
|
|
112
|
+
function sleep(ms) {
|
|
113
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
114
|
+
}
|
|
115
|
+
async function gzipCompress(input) {
|
|
116
|
+
const encoder = new TextEncoder();
|
|
117
|
+
const stream = new Blob([encoder.encode(input)]).stream().pipeThrough(new CompressionStream("gzip"));
|
|
118
|
+
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
119
|
+
}
|
|
120
|
+
async function compressPayload(payload, isDebug) {
|
|
121
|
+
const minified = JSON.stringify(payload);
|
|
122
|
+
if (isDebug) {
|
|
123
|
+
return minified;
|
|
124
|
+
}
|
|
125
|
+
return gzipCompress(minified);
|
|
126
|
+
}
|
|
127
|
+
var Transport = class {
|
|
128
|
+
constructor(endpoint, apiKey, projectId, fetch, isDebug = false) {
|
|
129
|
+
this.endpoint = endpoint;
|
|
130
|
+
this.apiKey = apiKey;
|
|
131
|
+
this.projectId = projectId;
|
|
132
|
+
this.fetch = fetch ?? getFetch();
|
|
133
|
+
this.isDebug = isDebug;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Send a batch of logs to the API
|
|
137
|
+
* Implements retry logic with exponential backoff
|
|
138
|
+
*/
|
|
139
|
+
async sendBatch(logs) {
|
|
140
|
+
if (logs.length === 0) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const payload = {
|
|
144
|
+
projectId: this.projectId,
|
|
145
|
+
logs
|
|
146
|
+
};
|
|
147
|
+
let lastError = null;
|
|
148
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
149
|
+
try {
|
|
150
|
+
const body = await compressPayload(payload, this.isDebug);
|
|
151
|
+
const headers = {
|
|
152
|
+
"Content-Type": "application/json"
|
|
153
|
+
};
|
|
154
|
+
if (this.apiKey) {
|
|
155
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
156
|
+
}
|
|
157
|
+
if (!this.isDebug) {
|
|
158
|
+
headers["Content-Encoding"] = "gzip";
|
|
159
|
+
}
|
|
160
|
+
const response = await this.fetch(`${this.endpoint}/logs`, {
|
|
161
|
+
method: "POST",
|
|
162
|
+
headers,
|
|
163
|
+
body
|
|
164
|
+
});
|
|
165
|
+
if (response.ok) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
|
169
|
+
lastError = new Error(`HTTP ${response.status}: ${errorText}`);
|
|
170
|
+
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
} catch (error) {
|
|
174
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
175
|
+
}
|
|
176
|
+
if (attempt < MAX_RETRIES - 1) {
|
|
177
|
+
const delay = calculateBackoffDelay(attempt);
|
|
178
|
+
await sleep(delay);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (process.env.NODE_ENV === "development") {
|
|
182
|
+
console.warn("[Gunsole] Failed to send logs after retries:", lastError);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// src/client.ts
|
|
188
|
+
var GunsoleClient = class {
|
|
189
|
+
constructor(config) {
|
|
190
|
+
this.batch = [];
|
|
191
|
+
this.flushTimer = null;
|
|
192
|
+
this.user = null;
|
|
193
|
+
this.sessionId = null;
|
|
194
|
+
this.globalHandlers = { attached: false };
|
|
195
|
+
this.config = normalizeConfig(config);
|
|
196
|
+
this.disabled = config.isDisabled ?? false;
|
|
197
|
+
this.transport = new Transport(
|
|
198
|
+
this.config.endpoint,
|
|
199
|
+
this.config.apiKey,
|
|
200
|
+
this.config.projectId,
|
|
201
|
+
this.config.fetch,
|
|
202
|
+
config.isDebug ?? false
|
|
203
|
+
);
|
|
204
|
+
if (this.disabled) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
this.startFlushTimer();
|
|
208
|
+
}
|
|
209
|
+
log(levelOrOptions, maybeOptions) {
|
|
210
|
+
if (this.disabled) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const level = typeof levelOrOptions === "string" ? levelOrOptions : "info";
|
|
214
|
+
const options = typeof levelOrOptions === "string" ? maybeOptions : levelOrOptions;
|
|
215
|
+
try {
|
|
216
|
+
const internalEntry = {
|
|
217
|
+
level,
|
|
218
|
+
bucket: options.bucket,
|
|
219
|
+
message: options.message,
|
|
220
|
+
context: options.context,
|
|
221
|
+
timestamp: Date.now(),
|
|
222
|
+
traceId: options.traceId,
|
|
223
|
+
userId: this.user?.id,
|
|
224
|
+
sessionId: this.sessionId ?? void 0,
|
|
225
|
+
env: this.config.env || void 0,
|
|
226
|
+
appName: this.config.appName || void 0,
|
|
227
|
+
appVersion: this.config.appVersion || void 0,
|
|
228
|
+
tags: {
|
|
229
|
+
...this.config.defaultTags,
|
|
230
|
+
...Array.isArray(options.tags) ? Object.assign({}, ...options.tags) : options.tags
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
this.batch.push(internalEntry);
|
|
234
|
+
if (this.batch.length >= this.config.batchSize) {
|
|
235
|
+
this.flush();
|
|
236
|
+
}
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (process.env.NODE_ENV === "development") {
|
|
239
|
+
console.warn("[Gunsole] Error in log():", error);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Log an info-level message
|
|
245
|
+
*/
|
|
246
|
+
info(options) {
|
|
247
|
+
this.log("info", options);
|
|
248
|
+
}
|
|
249
|
+
debug(options) {
|
|
250
|
+
this.log("debug", options);
|
|
251
|
+
}
|
|
252
|
+
warn(options) {
|
|
253
|
+
this.log("warn", options);
|
|
254
|
+
}
|
|
255
|
+
error(options) {
|
|
256
|
+
this.log("error", options);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Set user information
|
|
260
|
+
*/
|
|
261
|
+
setUser(user) {
|
|
262
|
+
if (this.disabled) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
this.user = user;
|
|
267
|
+
} catch (error) {
|
|
268
|
+
if (process.env.NODE_ENV === "development") {
|
|
269
|
+
console.warn("[Gunsole] Error in setUser():", error);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Set session ID
|
|
275
|
+
*/
|
|
276
|
+
setSessionId(sessionId) {
|
|
277
|
+
if (this.disabled) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
try {
|
|
281
|
+
this.sessionId = sessionId;
|
|
282
|
+
} catch (error) {
|
|
283
|
+
if (process.env.NODE_ENV === "development") {
|
|
284
|
+
console.warn("[Gunsole] Error in setSessionId():", error);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Flush pending logs to the API
|
|
290
|
+
*/
|
|
291
|
+
async flush() {
|
|
292
|
+
if (this.disabled) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
if (this.batch.length === 0) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const logsToSend = [...this.batch];
|
|
300
|
+
this.batch = [];
|
|
301
|
+
await this.transport.sendBatch(logsToSend);
|
|
302
|
+
} catch (error) {
|
|
303
|
+
if (process.env.NODE_ENV === "development") {
|
|
304
|
+
console.warn("[Gunsole] Error in flush():", error);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Attach global error handlers
|
|
310
|
+
*/
|
|
311
|
+
attachGlobalErrorHandlers() {
|
|
312
|
+
if (this.disabled) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
if (this.globalHandlers.attached) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
this.globalHandlers.unhandledRejection = (event) => {
|
|
320
|
+
this.error({
|
|
321
|
+
message: "Unhandled promise rejection",
|
|
322
|
+
bucket: "unhandled_rejection",
|
|
323
|
+
context: {
|
|
324
|
+
reason: String(event.reason),
|
|
325
|
+
error: event.reason instanceof Error ? {
|
|
326
|
+
name: event.reason.name,
|
|
327
|
+
message: event.reason.message,
|
|
328
|
+
stack: event.reason.stack
|
|
329
|
+
} : event.reason
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
};
|
|
333
|
+
this.globalHandlers.error = (event) => {
|
|
334
|
+
this.error({
|
|
335
|
+
message: event.message || "Global error",
|
|
336
|
+
bucket: "global_error",
|
|
337
|
+
context: {
|
|
338
|
+
filename: event.filename,
|
|
339
|
+
lineno: event.lineno,
|
|
340
|
+
colno: event.colno,
|
|
341
|
+
error: event.error ? {
|
|
342
|
+
name: event.error.name,
|
|
343
|
+
message: event.error.message,
|
|
344
|
+
stack: event.error.stack
|
|
345
|
+
} : void 0
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
};
|
|
349
|
+
if (typeof window !== "undefined") {
|
|
350
|
+
window.addEventListener(
|
|
351
|
+
"unhandledrejection",
|
|
352
|
+
this.globalHandlers.unhandledRejection
|
|
353
|
+
);
|
|
354
|
+
window.addEventListener("error", this.globalHandlers.error);
|
|
355
|
+
}
|
|
356
|
+
if (typeof process !== "undefined") {
|
|
357
|
+
this.globalHandlers.unhandledRejectionNode = (reason, _promise) => {
|
|
358
|
+
this.error({
|
|
359
|
+
message: "Unhandled promise rejection",
|
|
360
|
+
bucket: "unhandled_rejection",
|
|
361
|
+
context: {
|
|
362
|
+
reason: String(reason),
|
|
363
|
+
error: reason instanceof Error ? {
|
|
364
|
+
name: reason.name,
|
|
365
|
+
message: reason.message,
|
|
366
|
+
stack: reason.stack
|
|
367
|
+
} : reason
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
};
|
|
371
|
+
this.globalHandlers.uncaughtException = (error) => {
|
|
372
|
+
this.error({
|
|
373
|
+
message: error.message,
|
|
374
|
+
bucket: "uncaught_exception",
|
|
375
|
+
context: {
|
|
376
|
+
name: error.name,
|
|
377
|
+
stack: error.stack
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
};
|
|
381
|
+
process.on(
|
|
382
|
+
"unhandledRejection",
|
|
383
|
+
this.globalHandlers.unhandledRejectionNode
|
|
384
|
+
);
|
|
385
|
+
process.on("uncaughtException", this.globalHandlers.uncaughtException);
|
|
386
|
+
}
|
|
387
|
+
this.globalHandlers.attached = true;
|
|
388
|
+
} catch (error) {
|
|
389
|
+
if (process.env.NODE_ENV === "development") {
|
|
390
|
+
console.warn("[Gunsole] Error in attachGlobalErrorHandlers():", error);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Detach global error handlers
|
|
396
|
+
*/
|
|
397
|
+
detachGlobalErrorHandlers() {
|
|
398
|
+
try {
|
|
399
|
+
if (!this.globalHandlers.attached) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (typeof window !== "undefined") {
|
|
403
|
+
if (this.globalHandlers.unhandledRejection) {
|
|
404
|
+
window.removeEventListener(
|
|
405
|
+
"unhandledrejection",
|
|
406
|
+
this.globalHandlers.unhandledRejection
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
if (this.globalHandlers.error) {
|
|
410
|
+
window.removeEventListener("error", this.globalHandlers.error);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (typeof process !== "undefined") {
|
|
414
|
+
if (this.globalHandlers.unhandledRejectionNode) {
|
|
415
|
+
process.removeListener(
|
|
416
|
+
"unhandledRejection",
|
|
417
|
+
this.globalHandlers.unhandledRejectionNode
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
if (this.globalHandlers.uncaughtException) {
|
|
421
|
+
process.removeListener(
|
|
422
|
+
"uncaughtException",
|
|
423
|
+
this.globalHandlers.uncaughtException
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
this.globalHandlers = { attached: false };
|
|
428
|
+
} catch (error) {
|
|
429
|
+
if (process.env.NODE_ENV === "development") {
|
|
430
|
+
console.warn("[Gunsole] Error in detachGlobalErrorHandlers():", error);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Start the automatic flush timer
|
|
436
|
+
*/
|
|
437
|
+
startFlushTimer() {
|
|
438
|
+
if (this.flushTimer) {
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
this.flushTimer = setInterval(() => {
|
|
442
|
+
this.flush();
|
|
443
|
+
}, this.config.flushInterval);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Stop the automatic flush timer
|
|
447
|
+
*/
|
|
448
|
+
stopFlushTimer() {
|
|
449
|
+
if (this.flushTimer) {
|
|
450
|
+
clearInterval(this.flushTimer);
|
|
451
|
+
this.flushTimer = null;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Cleanup resources. Awaiting ensures remaining logs are flushed.
|
|
456
|
+
*/
|
|
457
|
+
async destroy() {
|
|
458
|
+
this.stopFlushTimer();
|
|
459
|
+
this.detachGlobalErrorHandlers();
|
|
460
|
+
await this.flush();
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
// src/factory.ts
|
|
465
|
+
function createGunsoleClient(config) {
|
|
466
|
+
const client = new GunsoleClient(config);
|
|
467
|
+
const buckets = config.buckets ?? [];
|
|
468
|
+
if (buckets.length > 0) {
|
|
469
|
+
return attachBuckets(client, buckets);
|
|
470
|
+
}
|
|
471
|
+
return client;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
exports.GunsoleClient = GunsoleClient;
|
|
475
|
+
exports.createGunsoleClient = createGunsoleClient;
|
|
476
|
+
//# sourceMappingURL=index.cjs.map
|
|
477
|
+
//# sourceMappingURL=index.cjs.map
|