@bliplogs/sdk 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/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +356 -0
- package/dist/index.d.mts +185 -0
- package/dist/index.d.ts +185 -0
- package/dist/index.js +369 -0
- package/dist/index.mjs +344 -0
- package/package.json +66 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var _BlipLogs = class _BlipLogs {
|
|
3
|
+
constructor(config) {
|
|
4
|
+
if (!config.apiKey) {
|
|
5
|
+
throw new Error("BlipLogs: apiKey is required");
|
|
6
|
+
}
|
|
7
|
+
if (!config.projectId) {
|
|
8
|
+
throw new Error("BlipLogs: projectId is required");
|
|
9
|
+
}
|
|
10
|
+
this.apiKey = config.apiKey;
|
|
11
|
+
this.projectId = config.projectId;
|
|
12
|
+
this.debug = config.debug ?? false;
|
|
13
|
+
this.onError = config.onError;
|
|
14
|
+
this.privacy = {
|
|
15
|
+
anonymizeIp: config.privacy?.anonymizeIp ?? false,
|
|
16
|
+
collectUrl: config.privacy?.collectUrl ?? true,
|
|
17
|
+
collectReferrer: config.privacy?.collectReferrer ?? true,
|
|
18
|
+
collectUserAgent: config.privacy?.collectUserAgent ?? true,
|
|
19
|
+
collectSessionId: config.privacy?.collectSessionId ?? true
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Configure the global BlipLogs instance
|
|
24
|
+
* Call this once at the start of your application (e.g., in your Astro layout)
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* // In Astro layout or initialization script
|
|
29
|
+
* BlipLogs.configure({
|
|
30
|
+
* apiKey: import.meta.env.PUBLIC_BLIPLOGS_API_KEY,
|
|
31
|
+
* projectId: import.meta.env.PUBLIC_BLIPLOGS_PROJECT_ID
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
static configure(config) {
|
|
36
|
+
_BlipLogs.globalInstance = new _BlipLogs(config);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get the global BlipLogs instance
|
|
40
|
+
* Throws an error if not configured
|
|
41
|
+
*/
|
|
42
|
+
static getInstance() {
|
|
43
|
+
if (!_BlipLogs.globalInstance) {
|
|
44
|
+
throw new Error("BlipLogs: Global instance not configured. Call BlipLogs.configure() first.");
|
|
45
|
+
}
|
|
46
|
+
return _BlipLogs.globalInstance;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Track an event using the global instance
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* BlipLogs.track('button_clicked', { buttonId: 'signup' });
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
static track(event, metadata, level = "info") {
|
|
57
|
+
return _BlipLogs.getInstance().track(event, metadata, level);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Track an info-level event using the global instance
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* BlipLogs.info('page_viewed', { page: '/dashboard' });
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
static info(event, metadata) {
|
|
68
|
+
return _BlipLogs.getInstance().info(event, metadata);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Track a warn-level event using the global instance
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* BlipLogs.warn('slow_request', { duration: 5000 });
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
static warn(event, metadata) {
|
|
79
|
+
return _BlipLogs.getInstance().warn(event, metadata);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Track an error-level event using the global instance
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* BlipLogs.error('api_error', { message: 'Failed to fetch' });
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
static error(event, metadata) {
|
|
90
|
+
return _BlipLogs.getInstance().error(event, metadata);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Gets or creates a session ID from sessionStorage
|
|
94
|
+
* Sessions expire after 30 minutes of inactivity
|
|
95
|
+
*/
|
|
96
|
+
getSessionId() {
|
|
97
|
+
if (typeof window === "undefined" || typeof sessionStorage === "undefined") {
|
|
98
|
+
return void 0;
|
|
99
|
+
}
|
|
100
|
+
const SESSION_TIMEOUT = 30 * 60 * 1e3;
|
|
101
|
+
const STORAGE_KEY_ID = "blip_session_id";
|
|
102
|
+
const STORAGE_KEY_TS = "blip_session_ts";
|
|
103
|
+
try {
|
|
104
|
+
let sessionId = sessionStorage.getItem(STORAGE_KEY_ID);
|
|
105
|
+
const lastActivityStr = sessionStorage.getItem(STORAGE_KEY_TS);
|
|
106
|
+
const lastActivity = lastActivityStr ? parseInt(lastActivityStr, 10) : null;
|
|
107
|
+
const now = Date.now();
|
|
108
|
+
if (!sessionId || !lastActivity || now - lastActivity > SESSION_TIMEOUT) {
|
|
109
|
+
sessionId = crypto.randomUUID();
|
|
110
|
+
sessionStorage.setItem(STORAGE_KEY_ID, sessionId);
|
|
111
|
+
}
|
|
112
|
+
sessionStorage.setItem(STORAGE_KEY_TS, now.toString());
|
|
113
|
+
return sessionId;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
return void 0;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Sanitize a URL by removing sensitive query parameters
|
|
120
|
+
*/
|
|
121
|
+
sanitizeUrl(url) {
|
|
122
|
+
if (!url) return void 0;
|
|
123
|
+
try {
|
|
124
|
+
const urlObj = new URL(url);
|
|
125
|
+
const params = urlObj.searchParams;
|
|
126
|
+
const sensitiveSet = new Set(_BlipLogs.SENSITIVE_PARAMS.map((p) => p.toLowerCase()));
|
|
127
|
+
const paramsToRedact = [];
|
|
128
|
+
params.forEach((_, key) => {
|
|
129
|
+
if (sensitiveSet.has(key.toLowerCase())) {
|
|
130
|
+
paramsToRedact.push(key);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
for (const key of paramsToRedact) {
|
|
134
|
+
params.set(key, "[REDACTED]");
|
|
135
|
+
}
|
|
136
|
+
return urlObj.toString();
|
|
137
|
+
} catch {
|
|
138
|
+
const queryIndex = url.indexOf("?");
|
|
139
|
+
if (queryIndex !== -1) {
|
|
140
|
+
return url.substring(0, queryIndex) + "?[QUERY_REDACTED]";
|
|
141
|
+
}
|
|
142
|
+
return url;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Auto-captures browser context information with sensitive data sanitization
|
|
147
|
+
* Respects privacy configuration settings
|
|
148
|
+
*/
|
|
149
|
+
getContext() {
|
|
150
|
+
const baseContext = {
|
|
151
|
+
projectId: this.projectId
|
|
152
|
+
};
|
|
153
|
+
if (typeof window === "undefined") {
|
|
154
|
+
return baseContext;
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
...baseContext,
|
|
158
|
+
url: this.privacy.collectUrl ? this.sanitizeUrl(window.location?.href) : void 0,
|
|
159
|
+
referrer: this.privacy.collectReferrer ? this.sanitizeUrl(document.referrer) || void 0 : void 0,
|
|
160
|
+
userAgent: this.privacy.collectUserAgent ? navigator.userAgent : void 0
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Track an event with optional metadata and level
|
|
165
|
+
*
|
|
166
|
+
* @param event - The event name (e.g., 'signup_modal_opened')
|
|
167
|
+
* @param metadata - Optional custom data to attach to the event
|
|
168
|
+
* @param level - Event level: 'info' (default), 'warn', or 'error'
|
|
169
|
+
* @returns boolean - Whether the event was queued for delivery
|
|
170
|
+
*/
|
|
171
|
+
track(event, metadata, level = "info") {
|
|
172
|
+
if (!event) {
|
|
173
|
+
console.warn("BlipLogs: event name is required");
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
const now = Date.now();
|
|
177
|
+
const sessionId = this.privacy.collectSessionId ? this.getSessionId() : void 0;
|
|
178
|
+
const payload = {
|
|
179
|
+
event,
|
|
180
|
+
level,
|
|
181
|
+
metadata,
|
|
182
|
+
context: this.getContext(),
|
|
183
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
184
|
+
// Keep for backward compatibility
|
|
185
|
+
session_id: sessionId,
|
|
186
|
+
timestamp_ms: now,
|
|
187
|
+
// Numeric timestamp in milliseconds
|
|
188
|
+
anonymize_ip: this.privacy.anonymizeIp || void 0
|
|
189
|
+
// Only include if true
|
|
190
|
+
};
|
|
191
|
+
return this.send(payload);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Convenience method for info-level events
|
|
195
|
+
*/
|
|
196
|
+
info(event, metadata) {
|
|
197
|
+
return this.track(event, metadata, "info");
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Convenience method for warn-level events
|
|
201
|
+
*/
|
|
202
|
+
warn(event, metadata) {
|
|
203
|
+
return this.track(event, metadata, "warn");
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Convenience method for error-level events
|
|
207
|
+
*/
|
|
208
|
+
error(event, metadata) {
|
|
209
|
+
return this.track(event, metadata, "error");
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Sends the payload using sendBeacon for speed and reliability
|
|
213
|
+
* Falls back to fetch if sendBeacon is unavailable or blocked
|
|
214
|
+
*/
|
|
215
|
+
send(payload) {
|
|
216
|
+
const payloadWithKey = {
|
|
217
|
+
...payload,
|
|
218
|
+
api_key: this.apiKey
|
|
219
|
+
};
|
|
220
|
+
const body = JSON.stringify(payloadWithKey);
|
|
221
|
+
if (typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
222
|
+
try {
|
|
223
|
+
const blob = new Blob([body], { type: "application/json" });
|
|
224
|
+
const sent = navigator.sendBeacon(_BlipLogs.API_ENDPOINT, blob);
|
|
225
|
+
if (!sent) {
|
|
226
|
+
return this.sendWithFetch(body);
|
|
227
|
+
}
|
|
228
|
+
return true;
|
|
229
|
+
} catch (error) {
|
|
230
|
+
return this.sendWithFetch(body);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return this.sendWithFetch(body);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Handle and report errors
|
|
237
|
+
*/
|
|
238
|
+
handleError(error) {
|
|
239
|
+
if (this.debug) {
|
|
240
|
+
console.error(`BlipLogs Error [${error.type}]:`, error.message, error);
|
|
241
|
+
}
|
|
242
|
+
if (this.onError) {
|
|
243
|
+
try {
|
|
244
|
+
this.onError(error);
|
|
245
|
+
} catch (callbackError) {
|
|
246
|
+
if (this.debug) {
|
|
247
|
+
console.error("BlipLogs: Error in onError callback:", callbackError);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Fallback method using fetch API
|
|
254
|
+
*/
|
|
255
|
+
sendWithFetch(body) {
|
|
256
|
+
if (typeof fetch !== "undefined") {
|
|
257
|
+
fetch(_BlipLogs.API_ENDPOINT, {
|
|
258
|
+
method: "POST",
|
|
259
|
+
headers: {
|
|
260
|
+
"Content-Type": "application/json",
|
|
261
|
+
"x-api-key": this.apiKey
|
|
262
|
+
},
|
|
263
|
+
body,
|
|
264
|
+
keepalive: true,
|
|
265
|
+
credentials: "omit"
|
|
266
|
+
}).then((response) => {
|
|
267
|
+
if (!response.ok) {
|
|
268
|
+
let errorType = "unknown";
|
|
269
|
+
let message = `HTTP ${response.status}`;
|
|
270
|
+
if (response.status === 429) {
|
|
271
|
+
errorType = "rate_limit";
|
|
272
|
+
message = "Monthly event limit exceeded. Upgrade your plan to continue.";
|
|
273
|
+
} else if (response.status === 401) {
|
|
274
|
+
errorType = "auth";
|
|
275
|
+
message = "Invalid API key";
|
|
276
|
+
} else if (response.status === 400) {
|
|
277
|
+
errorType = "validation";
|
|
278
|
+
message = "Invalid request payload";
|
|
279
|
+
}
|
|
280
|
+
this.handleError({
|
|
281
|
+
type: errorType,
|
|
282
|
+
message,
|
|
283
|
+
statusCode: response.status
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}).catch((err) => {
|
|
287
|
+
this.handleError({
|
|
288
|
+
type: "network",
|
|
289
|
+
message: err?.message || "Network request failed",
|
|
290
|
+
originalError: err instanceof Error ? err : void 0
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
return true;
|
|
294
|
+
}
|
|
295
|
+
this.handleError({
|
|
296
|
+
type: "unknown",
|
|
297
|
+
message: "No suitable transport available (fetch not defined)"
|
|
298
|
+
});
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
_BlipLogs.globalInstance = null;
|
|
303
|
+
_BlipLogs.API_ENDPOINT = "https://api.bliplogs.co.uk";
|
|
304
|
+
/**
|
|
305
|
+
* Sensitive URL parameters that should be redacted
|
|
306
|
+
*/
|
|
307
|
+
_BlipLogs.SENSITIVE_PARAMS = [
|
|
308
|
+
"token",
|
|
309
|
+
"access_token",
|
|
310
|
+
"refresh_token",
|
|
311
|
+
"id_token",
|
|
312
|
+
"apikey",
|
|
313
|
+
"api_key",
|
|
314
|
+
"api-key",
|
|
315
|
+
"key",
|
|
316
|
+
"password",
|
|
317
|
+
"pwd",
|
|
318
|
+
"pass",
|
|
319
|
+
"secret",
|
|
320
|
+
"auth",
|
|
321
|
+
"authorization",
|
|
322
|
+
"bearer",
|
|
323
|
+
"session",
|
|
324
|
+
"sessionid",
|
|
325
|
+
"session_id",
|
|
326
|
+
"code",
|
|
327
|
+
"state",
|
|
328
|
+
"nonce",
|
|
329
|
+
// OAuth params
|
|
330
|
+
"email",
|
|
331
|
+
"phone",
|
|
332
|
+
"ssn",
|
|
333
|
+
// PII
|
|
334
|
+
"credit_card",
|
|
335
|
+
"cc",
|
|
336
|
+
"cvv",
|
|
337
|
+
"card"
|
|
338
|
+
];
|
|
339
|
+
var BlipLogs = _BlipLogs;
|
|
340
|
+
var index_default = BlipLogs;
|
|
341
|
+
export {
|
|
342
|
+
BlipLogs,
|
|
343
|
+
index_default as default
|
|
344
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bliplogs/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Zero-dependency event logging SDK for BlipLogs. Track events from browser and server with minimal overhead.",
|
|
5
|
+
"author": "BlipLogs <hello@bliplogs.co.uk>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://bliplogs.co.uk",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/NoSweatWebsites/bliplogs-sdk.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/NoSweatWebsites/bliplogs-sdk/issues"
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"module": "./dist/index.mjs",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"import": {
|
|
21
|
+
"types": "./dist/index.d.mts",
|
|
22
|
+
"default": "./dist/index.mjs"
|
|
23
|
+
},
|
|
24
|
+
"require": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"default": "./dist/index.js"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE",
|
|
34
|
+
"CHANGELOG.md"
|
|
35
|
+
],
|
|
36
|
+
"sideEffects": false,
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
42
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
43
|
+
"prepublishOnly": "npm run build"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"tsup": "^8.3.0",
|
|
47
|
+
"typescript": "^5.7.0"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"logging",
|
|
51
|
+
"events",
|
|
52
|
+
"analytics",
|
|
53
|
+
"bliplogs",
|
|
54
|
+
"event-tracking",
|
|
55
|
+
"telemetry",
|
|
56
|
+
"observability",
|
|
57
|
+
"monitoring",
|
|
58
|
+
"sendbeacon",
|
|
59
|
+
"fire-and-forget",
|
|
60
|
+
"zero-dependencies",
|
|
61
|
+
"debugging"
|
|
62
|
+
],
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public"
|
|
65
|
+
}
|
|
66
|
+
}
|