@sailfish-ai/recorder 1.7.35 → 1.7.41
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/eventStore.js +89 -31
- package/dist/inAppReportIssueModal.js +45 -21
- package/dist/index.js +128 -92
- package/dist/notifyEventStore.js +73 -19
- package/dist/sailfish-recorder.cjs.js +1 -1
- package/dist/sailfish-recorder.cjs.js.br +0 -0
- package/dist/sailfish-recorder.cjs.js.gz +0 -0
- package/dist/sailfish-recorder.es.js +1 -1
- package/dist/sailfish-recorder.es.js.br +0 -0
- package/dist/sailfish-recorder.es.js.gz +0 -0
- package/dist/sailfish-recorder.umd.js +1 -1
- package/dist/sailfish-recorder.umd.js.br +0 -0
- package/dist/sailfish-recorder.umd.js.gz +0 -0
- package/dist/segmentHelpers.js +150 -0
- package/dist/sendSailfishMessages.js +10 -0
- package/dist/types/inAppReportIssueModal.d.ts +6 -0
- package/dist/types/segmentHelpers.d.ts +10 -0
- package/dist/types/sendSailfishMessages.d.ts +1 -0
- package/dist/types/utils.d.ts +2 -0
- package/dist/utils.js +7 -0
- package/package.json +1 -1
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { identify, trackingEvent } from "./sendSailfishMessages";
|
|
2
|
+
function safeParseJson(text) {
|
|
3
|
+
if (typeof text !== "string" || !text.trim())
|
|
4
|
+
return undefined;
|
|
5
|
+
try {
|
|
6
|
+
return JSON.parse(text);
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function isSegmentHost(url) {
|
|
13
|
+
try {
|
|
14
|
+
const u = new URL(url, window.location.href);
|
|
15
|
+
// Recognize any Segment ingestion domain (US default or regional)
|
|
16
|
+
return (u.hostname === "api.segment.io" || u.hostname.endsWith(".segmentapis.com"));
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function pathType(url) {
|
|
23
|
+
try {
|
|
24
|
+
const u = new URL(url, window.location.href);
|
|
25
|
+
const path = u.pathname;
|
|
26
|
+
// Segment ingestion endpoints for identify and track (short and full paths)
|
|
27
|
+
if (path.endsWith("/v1/i") ||
|
|
28
|
+
path === "/i" ||
|
|
29
|
+
path.endsWith("/v1/identify")) {
|
|
30
|
+
return "identify";
|
|
31
|
+
}
|
|
32
|
+
if (path.endsWith("/v1/t") || path === "/t" || path.endsWith("/v1/track")) {
|
|
33
|
+
return "track";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// URL parsing failed
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Try to capture Segment payloads and mirror them to our identify/trackingEvent calls.
|
|
43
|
+
* - For /v1/i: call identify(userId, traits)
|
|
44
|
+
* - For /v1/t: call trackingEvent({ event, properties })
|
|
45
|
+
*/
|
|
46
|
+
export async function maybeCaptureSegment(url, bodyCandidate) {
|
|
47
|
+
if (!isSegmentHost(url))
|
|
48
|
+
return;
|
|
49
|
+
const type = pathType(url);
|
|
50
|
+
if (!type)
|
|
51
|
+
return;
|
|
52
|
+
// Body is usually text/plain JSON from browsers
|
|
53
|
+
let json;
|
|
54
|
+
if (typeof bodyCandidate === "string") {
|
|
55
|
+
json = safeParseJson(bodyCandidate);
|
|
56
|
+
}
|
|
57
|
+
else if (bodyCandidate &&
|
|
58
|
+
typeof bodyCandidate.text === "function") {
|
|
59
|
+
// BodyInit like Request/Blob/Response with .text(): read safely
|
|
60
|
+
try {
|
|
61
|
+
const txt = await bodyCandidate.text();
|
|
62
|
+
json = safeParseJson(txt);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
/* ignore */
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (bodyCandidate instanceof URLSearchParams) {
|
|
69
|
+
// Segment won't send this way normally, but no harm
|
|
70
|
+
json = safeParseJson(bodyCandidate.toString());
|
|
71
|
+
}
|
|
72
|
+
if (!json || typeof json !== "object")
|
|
73
|
+
return;
|
|
74
|
+
if (type === "identify") {
|
|
75
|
+
const userId = json.userId ?? json.user_id;
|
|
76
|
+
const traits = (json.traits ?? {});
|
|
77
|
+
if (typeof userId === "string" && userId) {
|
|
78
|
+
identify(userId, traits, false);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else if (type === "track") {
|
|
82
|
+
const event = json.event ?? json.name;
|
|
83
|
+
const properties = (json.properties ?? {});
|
|
84
|
+
if (typeof event === "string" && event) {
|
|
85
|
+
trackingEvent({ event, properties });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
90
|
+
// Convenience wrappers used by interceptors (centralized here to avoid dupes)
|
|
91
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
92
|
+
export function maybeMirrorSegmentFromXhr(url, sendArg) {
|
|
93
|
+
try {
|
|
94
|
+
if (!url)
|
|
95
|
+
return;
|
|
96
|
+
if (typeof sendArg === "string") {
|
|
97
|
+
void maybeCaptureSegment(url, sendArg);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
/* noop */
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export async function maybeMirrorSegmentFromFetchRequest(req) {
|
|
105
|
+
try {
|
|
106
|
+
const url = req?.url;
|
|
107
|
+
if (!url)
|
|
108
|
+
return;
|
|
109
|
+
// Clone so we don't consume the original body
|
|
110
|
+
const preview = req.clone();
|
|
111
|
+
const text = await preview.text().catch(() => undefined);
|
|
112
|
+
if (typeof text === "string") {
|
|
113
|
+
await maybeCaptureSegment(url, text);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
/* noop */
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export async function maybeMirrorSegmentFromFetchUrlAndInit(input, init) {
|
|
121
|
+
try {
|
|
122
|
+
const url = String(input);
|
|
123
|
+
const body = init?.body;
|
|
124
|
+
if (typeof body === "string") {
|
|
125
|
+
await maybeCaptureSegment(url, body);
|
|
126
|
+
}
|
|
127
|
+
else if (body && typeof body.text === "function") {
|
|
128
|
+
const txt = await body.text().catch(() => undefined);
|
|
129
|
+
if (typeof txt === "string") {
|
|
130
|
+
await maybeCaptureSegment(url, txt);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
/* noop */
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
export async function maybeMirrorSegmentFromFetchArgs(input, init) {
|
|
139
|
+
try {
|
|
140
|
+
if (input instanceof Request) {
|
|
141
|
+
await maybeMirrorSegmentFromFetchRequest(input);
|
|
142
|
+
}
|
|
143
|
+
else if (typeof input === "string" || input instanceof URL) {
|
|
144
|
+
await maybeMirrorSegmentFromFetchUrlAndInit(input, init);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
/* noop */
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { nowTimestamp } from "./utils";
|
|
1
2
|
import { sendMessage } from "./websocket";
|
|
2
3
|
// Internal state to track the last sent messages
|
|
3
4
|
let lastIdentifyMessage = null;
|
|
@@ -35,3 +36,12 @@ export function addOrUpdateMetadata(metadata) {
|
|
|
35
36
|
lastMetadataMessage = metadata;
|
|
36
37
|
sendMessage(message);
|
|
37
38
|
}
|
|
39
|
+
// Function to send trackingEvent message
|
|
40
|
+
export function trackingEvent(trackingData) {
|
|
41
|
+
const message = {
|
|
42
|
+
type: "trackingEvent",
|
|
43
|
+
trackingData,
|
|
44
|
+
timestamp: nowTimestamp(),
|
|
45
|
+
};
|
|
46
|
+
sendMessage(message);
|
|
47
|
+
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export declare const ReportIssueContext: {
|
|
2
|
+
resolveSessionId: (() => string) | null;
|
|
3
|
+
apiKey: string | null;
|
|
4
|
+
backendApi: string | null;
|
|
5
|
+
triageBaseUrl: string;
|
|
6
|
+
};
|
|
1
7
|
export declare function setupIssueReporting(options: {
|
|
2
8
|
apiKey: string;
|
|
3
9
|
backendApi: string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Try to capture Segment payloads and mirror them to our identify/trackingEvent calls.
|
|
3
|
+
* - For /v1/i: call identify(userId, traits)
|
|
4
|
+
* - For /v1/t: call trackingEvent({ event, properties })
|
|
5
|
+
*/
|
|
6
|
+
export declare function maybeCaptureSegment(url: string, bodyCandidate?: unknown): Promise<void>;
|
|
7
|
+
export declare function maybeMirrorSegmentFromXhr(url: string | null, sendArg: unknown): void;
|
|
8
|
+
export declare function maybeMirrorSegmentFromFetchRequest(req: Request): Promise<void>;
|
|
9
|
+
export declare function maybeMirrorSegmentFromFetchUrlAndInit(input: string | URL, init?: RequestInit): Promise<void>;
|
|
10
|
+
export declare function maybeMirrorSegmentFromFetchArgs(input: Request | string | URL, init?: RequestInit): Promise<void>;
|
package/dist/types/utils.d.ts
CHANGED
package/dist/utils.js
CHANGED
|
@@ -39,3 +39,10 @@ export function buildBatches(queue, getSize, maxBytes) {
|
|
|
39
39
|
export function eventSize(event) {
|
|
40
40
|
return new Blob([JSON.stringify(event)]).size;
|
|
41
41
|
}
|
|
42
|
+
// guard against old third party libraries which redefine Date.now
|
|
43
|
+
let nowTimestamp = Date.now;
|
|
44
|
+
if (!( /*@__PURE__*//[1-9][0-9]{12}/.test(Date.now().toString()))) {
|
|
45
|
+
// they have already redefined it! use a fallback
|
|
46
|
+
nowTimestamp = () => new Date().getTime();
|
|
47
|
+
}
|
|
48
|
+
export { nowTimestamp };
|