@hellsoft/vaka-node 0.2.0 → 0.3.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/dist/index.cjs +82 -1
- package/dist/index.d.ts +49 -0
- package/dist/index.mjs +80 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
FeedbackError: () => FeedbackError,
|
|
33
34
|
SDK: () => SDK,
|
|
34
35
|
VakaScopeSpanProcessor: () => VakaScopeSpanProcessor,
|
|
35
36
|
_globalScope: () => globalScope,
|
|
@@ -37,6 +38,7 @@ __export(index_exports, {
|
|
|
37
38
|
addBreadcrumb: () => addBreadcrumb,
|
|
38
39
|
captureError: () => captureError,
|
|
39
40
|
captureException: () => captureException,
|
|
41
|
+
captureFeedback: () => captureFeedback,
|
|
40
42
|
captureMessage: () => captureMessage,
|
|
41
43
|
close: () => close,
|
|
42
44
|
currentScope: () => currentScope,
|
|
@@ -556,7 +558,7 @@ function makeFastifyPlugin(capture, shouldCapture2) {
|
|
|
556
558
|
}
|
|
557
559
|
|
|
558
560
|
// src/index.ts
|
|
559
|
-
var SDK = { name: "vaka-node", version: "0.
|
|
561
|
+
var SDK = { name: "vaka-node", version: "0.3.0" };
|
|
560
562
|
var opts;
|
|
561
563
|
var transport;
|
|
562
564
|
var otelSdk;
|
|
@@ -751,6 +753,83 @@ function captureMessage(message, level = "info", ctx = {}) {
|
|
|
751
753
|
const item = { type: "error", ...baseItem(currentScope(), ctx), level, message, fingerprint: ctx.fingerprint };
|
|
752
754
|
return enqueue(item, level === "error" || level === "fatal") ? item.event_id : void 0;
|
|
753
755
|
}
|
|
756
|
+
var FeedbackError = class extends Error {
|
|
757
|
+
/** HTTP status, or 0 when nothing was sent. 429 means a rate limit was hit. */
|
|
758
|
+
status;
|
|
759
|
+
constructor(message, status = 0) {
|
|
760
|
+
super(message);
|
|
761
|
+
this.name = "FeedbackError";
|
|
762
|
+
this.status = status;
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
function contentTypeOf(filename) {
|
|
766
|
+
const ext = filename.toLowerCase().split(".").pop();
|
|
767
|
+
return ext === "png" ? "image/png" : ext === "webp" ? "image/webp" : "image/jpeg";
|
|
768
|
+
}
|
|
769
|
+
async function captureFeedback(f) {
|
|
770
|
+
if (!opts || !transport) throw new FeedbackError("vaka is not initialized");
|
|
771
|
+
const message = String(f?.message ?? "").trim();
|
|
772
|
+
if (!message) throw new FeedbackError("feedback needs a message");
|
|
773
|
+
const files = (f.attachments || []).filter(Boolean);
|
|
774
|
+
if (files.length > 3) throw new FeedbackError("at most 3 attachments per feedback");
|
|
775
|
+
const scope = currentScope();
|
|
776
|
+
const userId = f.userId || scope.user?.id;
|
|
777
|
+
const item = {
|
|
778
|
+
id: (0, import_node_crypto.randomUUID)(),
|
|
779
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
780
|
+
platform: "node",
|
|
781
|
+
source: "api",
|
|
782
|
+
message: message.slice(0, 5e3),
|
|
783
|
+
email: f.email?.trim() || void 0,
|
|
784
|
+
name: f.name?.trim() || void 0,
|
|
785
|
+
event_id: f.eventId,
|
|
786
|
+
issue: f.issue,
|
|
787
|
+
user_id: userId,
|
|
788
|
+
anon_id: userId ? void 0 : processId,
|
|
789
|
+
session_id: f.sessionId,
|
|
790
|
+
url: f.url || scope.request?.url,
|
|
791
|
+
release: opts.release,
|
|
792
|
+
environment: opts.environment,
|
|
793
|
+
tags: { ...scope.tags, ...f.tags || {} },
|
|
794
|
+
flags: Object.keys(scope.flags).length ? { ...scope.flags } : void 0
|
|
795
|
+
};
|
|
796
|
+
for (const k of Object.keys(item)) if (item[k] === void 0) delete item[k];
|
|
797
|
+
const headers2 = { "X-Vaka-Key": opts.key, "X-Vaka-Sdk": `${SDK.name}/${SDK.version}` };
|
|
798
|
+
if (f.ip) headers2["X-Forwarded-For"] = f.ip;
|
|
799
|
+
let body;
|
|
800
|
+
if (files.length) {
|
|
801
|
+
const form = new FormData();
|
|
802
|
+
form.append("feedback", JSON.stringify(item));
|
|
803
|
+
for (const a of files) {
|
|
804
|
+
const data = a.data instanceof ArrayBuffer ? new Uint8Array(a.data) : a.data;
|
|
805
|
+
form.append("attachment", new Blob([data], { type: a.contentType || contentTypeOf(a.filename) }), a.filename);
|
|
806
|
+
}
|
|
807
|
+
body = form;
|
|
808
|
+
} else {
|
|
809
|
+
headers2["Content-Type"] = "application/json";
|
|
810
|
+
body = JSON.stringify(item);
|
|
811
|
+
}
|
|
812
|
+
const { context: context2 } = await import("@opentelemetry/api");
|
|
813
|
+
const { suppressTracing: suppressTracing2 } = await import("@opentelemetry/core");
|
|
814
|
+
let r;
|
|
815
|
+
try {
|
|
816
|
+
r = await context2.with(
|
|
817
|
+
suppressTracing2(context2.active()),
|
|
818
|
+
() => fetch(`${opts.endpoint}/ingest/v1/feedback`, { method: "POST", headers: headers2, body, signal: AbortSignal.timeout(3e4) })
|
|
819
|
+
);
|
|
820
|
+
} catch (e) {
|
|
821
|
+
throw new FeedbackError(`vaka feedback: ${String(e?.message || e)}`);
|
|
822
|
+
}
|
|
823
|
+
const text = await r.text().catch(() => "");
|
|
824
|
+
let json = {};
|
|
825
|
+
try {
|
|
826
|
+
json = text ? JSON.parse(text) : {};
|
|
827
|
+
} catch {
|
|
828
|
+
}
|
|
829
|
+
if (r.status < 200 || r.status >= 300) throw new FeedbackError(`vaka feedback: HTTP ${r.status}${json.error ? `: ${json.error}` : ""}`, r.status);
|
|
830
|
+
scope.addBreadcrumb({ category: "feedback", message: "user feedback sent" }, opts.maxBreadcrumbs);
|
|
831
|
+
return { id: json.id || String(item.id), shortId: json.short_id || "" };
|
|
832
|
+
}
|
|
754
833
|
function track(name, props, o = {}) {
|
|
755
834
|
if (!opts || !name) return;
|
|
756
835
|
const scope = currentScope();
|
|
@@ -905,6 +984,7 @@ async function _reset() {
|
|
|
905
984
|
}
|
|
906
985
|
// Annotate the CommonJS export names for ESM import in node:
|
|
907
986
|
0 && (module.exports = {
|
|
987
|
+
FeedbackError,
|
|
908
988
|
SDK,
|
|
909
989
|
VakaScopeSpanProcessor,
|
|
910
990
|
_globalScope,
|
|
@@ -912,6 +992,7 @@ async function _reset() {
|
|
|
912
992
|
addBreadcrumb,
|
|
913
993
|
captureError,
|
|
914
994
|
captureException,
|
|
995
|
+
captureFeedback,
|
|
915
996
|
captureMessage,
|
|
916
997
|
close,
|
|
917
998
|
currentScope,
|
package/dist/index.d.ts
CHANGED
|
@@ -110,6 +110,55 @@ export declare function captureException(error: unknown, ctx?: CaptureContext, f
|
|
|
110
110
|
export declare const captureError: typeof captureException;
|
|
111
111
|
/** Report a message as an error-like event. */
|
|
112
112
|
export declare function captureMessage(message: string, level?: Level, ctx?: CaptureContext): string | undefined;
|
|
113
|
+
/** A file attached to feedback: PNG, JPEG or WebP, at most 5 MB. */
|
|
114
|
+
export interface FeedbackAttachment {
|
|
115
|
+
filename: string;
|
|
116
|
+
data: Uint8Array | ArrayBuffer;
|
|
117
|
+
/** Default: guessed from the file name. */
|
|
118
|
+
contentType?: string;
|
|
119
|
+
}
|
|
120
|
+
export interface Feedback {
|
|
121
|
+
/** What the user wrote (required). Longer than 5000 characters is cut. */
|
|
122
|
+
message: string;
|
|
123
|
+
/** Contact email, only when the user gave it. */
|
|
124
|
+
email?: string;
|
|
125
|
+
name?: string;
|
|
126
|
+
/** The error event this feedback is about (the id captureException returned). */
|
|
127
|
+
eventId?: string;
|
|
128
|
+
/** The issue this feedback is about: id or short id like `API-12`. */
|
|
129
|
+
issue?: string;
|
|
130
|
+
/** Default: the current scope's user. */
|
|
131
|
+
userId?: string;
|
|
132
|
+
/** The end user's session id, when your app has one. */
|
|
133
|
+
sessionId?: string;
|
|
134
|
+
/** Page or screen the feedback is about. Default: the current request's URL. */
|
|
135
|
+
url?: string;
|
|
136
|
+
/**
|
|
137
|
+
* The end user's IP address (e.g. `req.ip`). Feedback is rate limited per address, so without it
|
|
138
|
+
* all feedback from this server shares one limit.
|
|
139
|
+
*/
|
|
140
|
+
ip?: string;
|
|
141
|
+
tags?: Record<string, string>;
|
|
142
|
+
/** Up to 3 images. */
|
|
143
|
+
attachments?: FeedbackAttachment[];
|
|
144
|
+
}
|
|
145
|
+
export interface FeedbackResult {
|
|
146
|
+
id: string;
|
|
147
|
+
/** e.g. `API-F12` */
|
|
148
|
+
shortId: string;
|
|
149
|
+
}
|
|
150
|
+
/** Thrown by captureFeedback when the server rejects the feedback or cannot be reached. */
|
|
151
|
+
export declare class FeedbackError extends Error {
|
|
152
|
+
/** HTTP status, or 0 when nothing was sent. 429 means a rate limit was hit. */
|
|
153
|
+
status: number;
|
|
154
|
+
constructor(message: string, status?: number);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Forward feedback your users wrote (a support form, in-app chat) to Vaka, with the current
|
|
158
|
+
* scope's user, release, environment, tags and flags. Sent right away, not batched.
|
|
159
|
+
* Rejects with a FeedbackError when the server refuses it (e.g. 429 for a rate limit).
|
|
160
|
+
*/
|
|
161
|
+
export declare function captureFeedback(f: Feedback): Promise<FeedbackResult>;
|
|
113
162
|
/** Record a custom server-side event (e.g. `track('order_paid', { amount: 49 })`). */
|
|
114
163
|
export declare function track(name: string, props?: Props, o?: {
|
|
115
164
|
userId?: string;
|
package/dist/index.mjs
CHANGED
|
@@ -492,7 +492,7 @@ function makeFastifyPlugin(capture, shouldCapture2) {
|
|
|
492
492
|
}
|
|
493
493
|
|
|
494
494
|
// src/index.ts
|
|
495
|
-
var SDK = { name: "vaka-node", version: "0.
|
|
495
|
+
var SDK = { name: "vaka-node", version: "0.3.0" };
|
|
496
496
|
var opts;
|
|
497
497
|
var transport;
|
|
498
498
|
var otelSdk;
|
|
@@ -687,6 +687,83 @@ function captureMessage(message, level = "info", ctx = {}) {
|
|
|
687
687
|
const item = { type: "error", ...baseItem(currentScope(), ctx), level, message, fingerprint: ctx.fingerprint };
|
|
688
688
|
return enqueue(item, level === "error" || level === "fatal") ? item.event_id : void 0;
|
|
689
689
|
}
|
|
690
|
+
var FeedbackError = class extends Error {
|
|
691
|
+
/** HTTP status, or 0 when nothing was sent. 429 means a rate limit was hit. */
|
|
692
|
+
status;
|
|
693
|
+
constructor(message, status = 0) {
|
|
694
|
+
super(message);
|
|
695
|
+
this.name = "FeedbackError";
|
|
696
|
+
this.status = status;
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
function contentTypeOf(filename) {
|
|
700
|
+
const ext = filename.toLowerCase().split(".").pop();
|
|
701
|
+
return ext === "png" ? "image/png" : ext === "webp" ? "image/webp" : "image/jpeg";
|
|
702
|
+
}
|
|
703
|
+
async function captureFeedback(f) {
|
|
704
|
+
if (!opts || !transport) throw new FeedbackError("vaka is not initialized");
|
|
705
|
+
const message = String(f?.message ?? "").trim();
|
|
706
|
+
if (!message) throw new FeedbackError("feedback needs a message");
|
|
707
|
+
const files = (f.attachments || []).filter(Boolean);
|
|
708
|
+
if (files.length > 3) throw new FeedbackError("at most 3 attachments per feedback");
|
|
709
|
+
const scope = currentScope();
|
|
710
|
+
const userId = f.userId || scope.user?.id;
|
|
711
|
+
const item = {
|
|
712
|
+
id: randomUUID(),
|
|
713
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
714
|
+
platform: "node",
|
|
715
|
+
source: "api",
|
|
716
|
+
message: message.slice(0, 5e3),
|
|
717
|
+
email: f.email?.trim() || void 0,
|
|
718
|
+
name: f.name?.trim() || void 0,
|
|
719
|
+
event_id: f.eventId,
|
|
720
|
+
issue: f.issue,
|
|
721
|
+
user_id: userId,
|
|
722
|
+
anon_id: userId ? void 0 : processId,
|
|
723
|
+
session_id: f.sessionId,
|
|
724
|
+
url: f.url || scope.request?.url,
|
|
725
|
+
release: opts.release,
|
|
726
|
+
environment: opts.environment,
|
|
727
|
+
tags: { ...scope.tags, ...f.tags || {} },
|
|
728
|
+
flags: Object.keys(scope.flags).length ? { ...scope.flags } : void 0
|
|
729
|
+
};
|
|
730
|
+
for (const k of Object.keys(item)) if (item[k] === void 0) delete item[k];
|
|
731
|
+
const headers2 = { "X-Vaka-Key": opts.key, "X-Vaka-Sdk": `${SDK.name}/${SDK.version}` };
|
|
732
|
+
if (f.ip) headers2["X-Forwarded-For"] = f.ip;
|
|
733
|
+
let body;
|
|
734
|
+
if (files.length) {
|
|
735
|
+
const form = new FormData();
|
|
736
|
+
form.append("feedback", JSON.stringify(item));
|
|
737
|
+
for (const a of files) {
|
|
738
|
+
const data = a.data instanceof ArrayBuffer ? new Uint8Array(a.data) : a.data;
|
|
739
|
+
form.append("attachment", new Blob([data], { type: a.contentType || contentTypeOf(a.filename) }), a.filename);
|
|
740
|
+
}
|
|
741
|
+
body = form;
|
|
742
|
+
} else {
|
|
743
|
+
headers2["Content-Type"] = "application/json";
|
|
744
|
+
body = JSON.stringify(item);
|
|
745
|
+
}
|
|
746
|
+
const { context: context2 } = await import("@opentelemetry/api");
|
|
747
|
+
const { suppressTracing: suppressTracing2 } = await import("@opentelemetry/core");
|
|
748
|
+
let r;
|
|
749
|
+
try {
|
|
750
|
+
r = await context2.with(
|
|
751
|
+
suppressTracing2(context2.active()),
|
|
752
|
+
() => fetch(`${opts.endpoint}/ingest/v1/feedback`, { method: "POST", headers: headers2, body, signal: AbortSignal.timeout(3e4) })
|
|
753
|
+
);
|
|
754
|
+
} catch (e) {
|
|
755
|
+
throw new FeedbackError(`vaka feedback: ${String(e?.message || e)}`);
|
|
756
|
+
}
|
|
757
|
+
const text = await r.text().catch(() => "");
|
|
758
|
+
let json = {};
|
|
759
|
+
try {
|
|
760
|
+
json = text ? JSON.parse(text) : {};
|
|
761
|
+
} catch {
|
|
762
|
+
}
|
|
763
|
+
if (r.status < 200 || r.status >= 300) throw new FeedbackError(`vaka feedback: HTTP ${r.status}${json.error ? `: ${json.error}` : ""}`, r.status);
|
|
764
|
+
scope.addBreadcrumb({ category: "feedback", message: "user feedback sent" }, opts.maxBreadcrumbs);
|
|
765
|
+
return { id: json.id || String(item.id), shortId: json.short_id || "" };
|
|
766
|
+
}
|
|
690
767
|
function track(name, props, o = {}) {
|
|
691
768
|
if (!opts || !name) return;
|
|
692
769
|
const scope = currentScope();
|
|
@@ -840,6 +917,7 @@ async function _reset() {
|
|
|
840
917
|
_resetScopes();
|
|
841
918
|
}
|
|
842
919
|
export {
|
|
920
|
+
FeedbackError,
|
|
843
921
|
SDK,
|
|
844
922
|
VakaScopeSpanProcessor,
|
|
845
923
|
globalScope as _globalScope,
|
|
@@ -847,6 +925,7 @@ export {
|
|
|
847
925
|
addBreadcrumb,
|
|
848
926
|
captureError,
|
|
849
927
|
captureException,
|
|
928
|
+
captureFeedback,
|
|
850
929
|
captureMessage,
|
|
851
930
|
close,
|
|
852
931
|
currentScope,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hellsoft/vaka-node",
|
|
3
3
|
"publishConfig": {"access": "public"},
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "Vaka Node.js SDK: crash reporting, OpenTelemetry tracing and logs, feature flags",
|
|
6
6
|
"license": "MIT OR Apache-2.0",
|
|
7
7
|
"repository": {
|