@remote-logger/sdk 0.1.5 → 0.1.6

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/README.md CHANGED
@@ -144,6 +144,33 @@ logger.restoreConsole();
144
144
 
145
145
  This wraps `console.debug`, `console.log`, `console.info`, and `console.warn`. Errors are captured via `window.addEventListener('error')` and `unhandledrejection` listeners instead of wrapping `console.error`, to avoid polluting stack traces in frameworks like Next.js/React.
146
146
 
147
+ ## Source Map Support (Vite)
148
+
149
+ Automatically resolve minified production stack traces back to original source. The Vite plugin uploads source maps at build time, and the ingestion service resolves stack traces before storing them.
150
+
151
+ ```ts
152
+ // vite.config.ts
153
+ import remoteLoggerPlugin from '@remote-logger/sdk/vite';
154
+
155
+ export default defineConfig({
156
+ plugins: [
157
+ remoteLoggerPlugin({
158
+ apiKey: process.env.REMOTE_LOGGER_API_KEY!,
159
+ logFileId: 'your-log-file-id',
160
+ }),
161
+ ],
162
+ build: { sourcemap: true },
163
+ });
164
+ ```
165
+
166
+ The plugin:
167
+ - **Skips in dev mode** — stacks are already readable
168
+ - **Generates a release UUID** per build, injected as `__REMOTE_LOGGER_RELEASE__`
169
+ - **Uploads `.map` files** from the build output to the ingestion service on `closeBundle`
170
+ - **Multi-bundle (Electron):** All plugin instances in the same Node process share one release ID, so main + renderer maps are grouped under the same release
171
+
172
+ No SDK configuration needed — the SDK automatically detects `__REMOTE_LOGGER_RELEASE__` and attaches it to every log entry.
173
+
147
174
  ## AI Assistant Integration
148
175
 
149
176
  Run the init command to configure your AI coding assistant (Claude Code, Cursor, etc.) to use the SDK:
package/SKILL.md CHANGED
@@ -124,8 +124,9 @@ level String -- DEBUG, INFO, WARN, ERROR, FATAL
124
124
  trace_id String -- request correlation
125
125
  group String -- categorization (e.g., 'api/billing', 'main', 'renderer')
126
126
  message String -- log content
127
- stack Nullable(String) -- stack trace
127
+ stack Nullable(String) -- stack trace (resolved via source maps if available)
128
128
  error_type String -- exception class name
129
+ release_id String -- build release ID (for source map resolution)
129
130
  metadata JSON -- arbitrary structured data
130
131
  ```
131
132
 
@@ -0,0 +1,11 @@
1
+ var __typeError = (msg) => {
2
+ throw TypeError(msg);
3
+ };
4
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
5
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
6
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
7
+
8
+ export {
9
+ __privateAdd,
10
+ __privateMethod
11
+ };
package/dist/index.d.mts CHANGED
@@ -27,6 +27,7 @@ interface InternalLogEntry {
27
27
  trace_id?: string;
28
28
  stack?: string;
29
29
  error_type?: string;
30
+ release_id?: string;
30
31
  metadata?: Record<string, unknown>;
31
32
  }
32
33
  /** Object form for logger.info({ message, group?, metadata? }) */
package/dist/index.d.ts CHANGED
@@ -27,6 +27,7 @@ interface InternalLogEntry {
27
27
  trace_id?: string;
28
28
  stack?: string;
29
29
  error_type?: string;
30
+ release_id?: string;
30
31
  metadata?: Record<string, unknown>;
31
32
  }
32
33
  /** Object form for logger.info({ message, group?, metadata? }) */
package/dist/index.js CHANGED
@@ -339,6 +339,7 @@ function isErrorLike(value) {
339
339
  }
340
340
 
341
341
  // src/index.ts
342
+ var RELEASE_ID = typeof __REMOTE_LOGGER_RELEASE__ !== "undefined" ? __REMOTE_LOGGER_RELEASE__ : void 0;
342
343
  var LEVEL_ORDER = {
343
344
  DEBUG: 0,
344
345
  INFO: 1,
@@ -519,6 +520,7 @@ ${arg.stack}`;
519
520
  trace_id: options?.traceId ?? this.config.traceId,
520
521
  stack: options?.stack,
521
522
  error_type: options?.errorType,
523
+ release_id: RELEASE_ID,
522
524
  metadata: options?.metadata
523
525
  };
524
526
  this.buffer.push(entry);
package/dist/index.mjs CHANGED
@@ -1,9 +1,7 @@
1
- var __typeError = (msg) => {
2
- throw TypeError(msg);
3
- };
4
- var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
5
- var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
6
- var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
1
+ import {
2
+ __privateAdd,
3
+ __privateMethod
4
+ } from "./chunk-JKIIIVNW.mjs";
7
5
 
8
6
  // ../node_modules/non-error/index.js
9
7
  var isNonErrorSymbol = /* @__PURE__ */ Symbol("isNonError");
@@ -312,6 +310,7 @@ function isErrorLike(value) {
312
310
  }
313
311
 
314
312
  // src/index.ts
313
+ var RELEASE_ID = typeof __REMOTE_LOGGER_RELEASE__ !== "undefined" ? __REMOTE_LOGGER_RELEASE__ : void 0;
315
314
  var LEVEL_ORDER = {
316
315
  DEBUG: 0,
317
316
  INFO: 1,
@@ -492,6 +491,7 @@ ${arg.stack}`;
492
491
  trace_id: options?.traceId ?? this.config.traceId,
493
492
  stack: options?.stack,
494
493
  error_type: options?.errorType,
494
+ release_id: RELEASE_ID,
495
495
  metadata: options?.metadata
496
496
  };
497
497
  this.buffer.push(entry);
@@ -0,0 +1,11 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ interface RemoteLoggerPluginOptions {
4
+ apiKey: string;
5
+ logFileId: string;
6
+ /** @internal Override endpoint for development */
7
+ _endpoint?: string;
8
+ }
9
+ declare function remoteLoggerPlugin(options: RemoteLoggerPluginOptions): Plugin;
10
+
11
+ export { type RemoteLoggerPluginOptions, remoteLoggerPlugin as default };
package/dist/vite.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ interface RemoteLoggerPluginOptions {
4
+ apiKey: string;
5
+ logFileId: string;
6
+ /** @internal Override endpoint for development */
7
+ _endpoint?: string;
8
+ }
9
+ declare function remoteLoggerPlugin(options: RemoteLoggerPluginOptions): Plugin;
10
+
11
+ export { type RemoteLoggerPluginOptions, remoteLoggerPlugin as default };
package/dist/vite.js ADDED
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/vite.ts
21
+ var vite_exports = {};
22
+ __export(vite_exports, {
23
+ default: () => remoteLoggerPlugin
24
+ });
25
+ module.exports = __toCommonJS(vite_exports);
26
+ var import_crypto = require("crypto");
27
+ var import_fs = require("fs");
28
+ var import_path = require("path");
29
+ var DEFAULT_ENDPOINT = "https://log.terodato.com";
30
+ function collectMapFiles(dir) {
31
+ const results = [];
32
+ try {
33
+ const entries = (0, import_fs.readdirSync)(dir, { withFileTypes: true });
34
+ for (const entry of entries) {
35
+ const fullPath = (0, import_path.join)(dir, entry.name);
36
+ if (entry.isDirectory()) {
37
+ results.push(...collectMapFiles(fullPath));
38
+ } else if (entry.name.endsWith(".map")) {
39
+ results.push(fullPath);
40
+ }
41
+ }
42
+ } catch {
43
+ }
44
+ return results;
45
+ }
46
+ var sharedReleaseId = null;
47
+ function remoteLoggerPlugin(options) {
48
+ if (!sharedReleaseId) sharedReleaseId = (0, import_crypto.randomUUID)();
49
+ const releaseId = sharedReleaseId;
50
+ let resolvedConfig;
51
+ let skip = false;
52
+ return {
53
+ name: "remote-logger-sourcemap",
54
+ enforce: "post",
55
+ config(config, { command, mode }) {
56
+ if (mode === "development" || command === "serve") {
57
+ skip = true;
58
+ return;
59
+ }
60
+ return {
61
+ define: {
62
+ __REMOTE_LOGGER_RELEASE__: JSON.stringify(releaseId)
63
+ }
64
+ };
65
+ },
66
+ configResolved(config) {
67
+ resolvedConfig = config;
68
+ },
69
+ async closeBundle() {
70
+ if (skip) return;
71
+ const outDir = resolvedConfig.build.outDir;
72
+ const mapFiles = collectMapFiles(outDir);
73
+ if (mapFiles.length === 0) {
74
+ console.log("[RemoteLogger] No source map files found, skipping upload");
75
+ return;
76
+ }
77
+ const endpoint = options._endpoint || DEFAULT_ENDPOINT;
78
+ const url = `${endpoint}/sourcemaps/upload`;
79
+ const formData = new FormData();
80
+ formData.append("releaseId", releaseId);
81
+ formData.append("logFileId", options.logFileId);
82
+ for (const mapFile of mapFiles) {
83
+ const content = (0, import_fs.readFileSync)(mapFile);
84
+ const name = (0, import_path.relative)(outDir, mapFile).replace(/\\/g, "/").replace(/\//g, "_");
85
+ const blob = new Blob([content], { type: "application/json" });
86
+ formData.append("files", blob, name.endsWith(".map") ? name : `${name}.map`);
87
+ }
88
+ try {
89
+ console.log(`[RemoteLogger] Uploading ${mapFiles.length} source maps (release: ${releaseId})...`);
90
+ const response = await fetch(url, {
91
+ method: "POST",
92
+ headers: {
93
+ "Authorization": `Bearer ${options.apiKey}`
94
+ },
95
+ body: formData
96
+ });
97
+ if (!response.ok) {
98
+ const body = await response.text();
99
+ console.error(`[RemoteLogger] Source map upload failed (${response.status}): ${body}`);
100
+ return;
101
+ }
102
+ const result = await response.json();
103
+ console.log(`[RemoteLogger] Uploaded ${result.count} source maps successfully`);
104
+ } catch (err) {
105
+ console.error("[RemoteLogger] Source map upload failed:", err);
106
+ }
107
+ }
108
+ };
109
+ }
package/dist/vite.mjs ADDED
@@ -0,0 +1,90 @@
1
+ import "./chunk-JKIIIVNW.mjs";
2
+
3
+ // src/vite.ts
4
+ import { randomUUID } from "crypto";
5
+ import { readdirSync, readFileSync } from "fs";
6
+ import { join, relative } from "path";
7
+ var DEFAULT_ENDPOINT = "https://log.terodato.com";
8
+ function collectMapFiles(dir) {
9
+ const results = [];
10
+ try {
11
+ const entries = readdirSync(dir, { withFileTypes: true });
12
+ for (const entry of entries) {
13
+ const fullPath = join(dir, entry.name);
14
+ if (entry.isDirectory()) {
15
+ results.push(...collectMapFiles(fullPath));
16
+ } else if (entry.name.endsWith(".map")) {
17
+ results.push(fullPath);
18
+ }
19
+ }
20
+ } catch {
21
+ }
22
+ return results;
23
+ }
24
+ var sharedReleaseId = null;
25
+ function remoteLoggerPlugin(options) {
26
+ if (!sharedReleaseId) sharedReleaseId = randomUUID();
27
+ const releaseId = sharedReleaseId;
28
+ let resolvedConfig;
29
+ let skip = false;
30
+ return {
31
+ name: "remote-logger-sourcemap",
32
+ enforce: "post",
33
+ config(config, { command, mode }) {
34
+ if (mode === "development" || command === "serve") {
35
+ skip = true;
36
+ return;
37
+ }
38
+ return {
39
+ define: {
40
+ __REMOTE_LOGGER_RELEASE__: JSON.stringify(releaseId)
41
+ }
42
+ };
43
+ },
44
+ configResolved(config) {
45
+ resolvedConfig = config;
46
+ },
47
+ async closeBundle() {
48
+ if (skip) return;
49
+ const outDir = resolvedConfig.build.outDir;
50
+ const mapFiles = collectMapFiles(outDir);
51
+ if (mapFiles.length === 0) {
52
+ console.log("[RemoteLogger] No source map files found, skipping upload");
53
+ return;
54
+ }
55
+ const endpoint = options._endpoint || DEFAULT_ENDPOINT;
56
+ const url = `${endpoint}/sourcemaps/upload`;
57
+ const formData = new FormData();
58
+ formData.append("releaseId", releaseId);
59
+ formData.append("logFileId", options.logFileId);
60
+ for (const mapFile of mapFiles) {
61
+ const content = readFileSync(mapFile);
62
+ const name = relative(outDir, mapFile).replace(/\\/g, "/").replace(/\//g, "_");
63
+ const blob = new Blob([content], { type: "application/json" });
64
+ formData.append("files", blob, name.endsWith(".map") ? name : `${name}.map`);
65
+ }
66
+ try {
67
+ console.log(`[RemoteLogger] Uploading ${mapFiles.length} source maps (release: ${releaseId})...`);
68
+ const response = await fetch(url, {
69
+ method: "POST",
70
+ headers: {
71
+ "Authorization": `Bearer ${options.apiKey}`
72
+ },
73
+ body: formData
74
+ });
75
+ if (!response.ok) {
76
+ const body = await response.text();
77
+ console.error(`[RemoteLogger] Source map upload failed (${response.status}): ${body}`);
78
+ return;
79
+ }
80
+ const result = await response.json();
81
+ console.log(`[RemoteLogger] Uploaded ${result.count} source maps successfully`);
82
+ } catch (err) {
83
+ console.error("[RemoteLogger] Source map upload failed:", err);
84
+ }
85
+ }
86
+ };
87
+ }
88
+ export {
89
+ remoteLoggerPlugin as default
90
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remote-logger/sdk",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "JavaScript SDK for Remote Logger",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -10,6 +10,11 @@
10
10
  "types": "./dist/index.d.ts",
11
11
  "require": "./dist/index.js",
12
12
  "import": "./dist/index.mjs"
13
+ },
14
+ "./vite": {
15
+ "types": "./dist/vite.d.ts",
16
+ "require": "./dist/vite.js",
17
+ "import": "./dist/vite.mjs"
13
18
  }
14
19
  },
15
20
  "bin": {
@@ -20,7 +25,7 @@
20
25
  "SKILL.md"
21
26
  ],
22
27
  "scripts": {
23
- "build": "tsup src/index.ts --format cjs,esm --dts && tsup src/init.ts --format cjs --no-dts",
28
+ "build": "tsup src/index.ts src/vite.ts --format cjs,esm --dts && tsup src/init.ts --format cjs --no-dts",
24
29
  "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
25
30
  "test": "tsx test/test.ts"
26
31
  },
@@ -28,7 +33,16 @@
28
33
  "serialize-error": "^13.0.1",
29
34
  "tsup": "^8.0.0",
30
35
  "tsx": "^4.7.0",
31
- "typescript": "^5.3.0"
36
+ "typescript": "^5.3.0",
37
+ "vite": "^7.3.1"
38
+ },
39
+ "peerDependencies": {
40
+ "vite": ">=5.0.0"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "vite": {
44
+ "optional": true
45
+ }
32
46
  },
33
47
  "keywords": [
34
48
  "logging",