@shipeasy/sdk 3.1.0 → 4.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/README.md +52 -0
- package/dist/client/index.d.mts +149 -7
- package/dist/client/index.d.ts +149 -7
- package/dist/client/index.js +321 -50
- package/dist/client/index.mjs +320 -50
- package/dist/server/index.d.mts +112 -2
- package/dist/server/index.d.ts +112 -2
- package/dist/server/index.js +235 -1
- package/dist/server/index.mjs +234 -1
- package/package.json +1 -1
package/dist/client/index.js
CHANGED
|
@@ -38,6 +38,7 @@ __export(client_exports, {
|
|
|
38
38
|
readConfigOverride: () => readConfigOverride,
|
|
39
39
|
readExpOverride: () => readExpOverride,
|
|
40
40
|
readGateOverride: () => readGateOverride,
|
|
41
|
+
see: () => see,
|
|
41
42
|
shipeasy: () => shipeasy,
|
|
42
43
|
version: () => version
|
|
43
44
|
});
|
|
@@ -102,8 +103,208 @@ function send(url) {
|
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
// src/see/core.ts
|
|
107
|
+
var SEE_MAX_MESSAGE = 500;
|
|
108
|
+
var SEE_MAX_STACK = 8e3;
|
|
109
|
+
var SEE_MAX_SUBJECT = 200;
|
|
110
|
+
var SEE_MAX_EXTRA_VALUE = 200;
|
|
111
|
+
var SEE_MAX_EXTRA_KEYS = 20;
|
|
112
|
+
var SEE_DEDUP_WINDOW_MS = 3e4;
|
|
113
|
+
var SEE_MAX_PER_SESSION = 25;
|
|
114
|
+
function causesThe(subject) {
|
|
115
|
+
return {
|
|
116
|
+
to(outcome) {
|
|
117
|
+
return {
|
|
118
|
+
__seConsequence: true,
|
|
119
|
+
subject: truncate(String(subject), SEE_MAX_SUBJECT),
|
|
120
|
+
outcome: truncate(String(outcome), SEE_MAX_SUBJECT)
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function violation(name) {
|
|
126
|
+
const make = (msg) => ({
|
|
127
|
+
__seViolation: true,
|
|
128
|
+
violationName: String(name),
|
|
129
|
+
...msg !== void 0 ? { violationMessage: msg } : {},
|
|
130
|
+
message(m) {
|
|
131
|
+
return make(String(m));
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
return make();
|
|
135
|
+
}
|
|
136
|
+
function isViolation(p) {
|
|
137
|
+
return typeof p === "object" && p !== null && p.__seViolation === true;
|
|
138
|
+
}
|
|
139
|
+
var EXPECTED_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:see-expected");
|
|
140
|
+
function markExpected(err, because) {
|
|
141
|
+
if (typeof err !== "object" || err === null) return;
|
|
142
|
+
try {
|
|
143
|
+
Object.defineProperty(err, EXPECTED_SYM, {
|
|
144
|
+
value: String(because),
|
|
145
|
+
enumerable: false,
|
|
146
|
+
configurable: true
|
|
147
|
+
});
|
|
148
|
+
} catch {
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function isExpected(err) {
|
|
152
|
+
if (typeof err !== "object" || err === null) return false;
|
|
153
|
+
return err[EXPECTED_SYM] !== void 0;
|
|
154
|
+
}
|
|
155
|
+
function truncate(s, max) {
|
|
156
|
+
return s.length > max ? s.slice(0, max) : s;
|
|
157
|
+
}
|
|
158
|
+
function sanitizeExtras(extras) {
|
|
159
|
+
if (!extras || typeof extras !== "object") return void 0;
|
|
160
|
+
const out = {};
|
|
161
|
+
let n = 0;
|
|
162
|
+
for (const [k, v] of Object.entries(extras)) {
|
|
163
|
+
if (v === null || v === void 0) continue;
|
|
164
|
+
if (n >= SEE_MAX_EXTRA_KEYS) break;
|
|
165
|
+
if (typeof v === "string") out[k] = truncate(v, SEE_MAX_EXTRA_VALUE);
|
|
166
|
+
else if (typeof v === "number" && Number.isFinite(v)) out[k] = v;
|
|
167
|
+
else if (typeof v === "boolean") out[k] = v;
|
|
168
|
+
else continue;
|
|
169
|
+
n += 1;
|
|
170
|
+
}
|
|
171
|
+
return n > 0 ? out : void 0;
|
|
172
|
+
}
|
|
173
|
+
function captureCallsiteStack() {
|
|
174
|
+
const raw = new Error().stack;
|
|
175
|
+
if (!raw) return void 0;
|
|
176
|
+
const lines = raw.split("\n");
|
|
177
|
+
const kept = lines.slice(1).filter((l) => !/@shipeasy[\\/]sdk|see[\\/]core|captureCallsiteStack|\bsee\b\s*\(/.test(l));
|
|
178
|
+
return kept.length ? kept.join("\n") : void 0;
|
|
179
|
+
}
|
|
180
|
+
function buildSeeEvent(problem, consequence, extras, ctx, kindOverride) {
|
|
181
|
+
let errorType;
|
|
182
|
+
let message;
|
|
183
|
+
let stack;
|
|
184
|
+
let kind;
|
|
185
|
+
if (isViolation(problem)) {
|
|
186
|
+
errorType = problem.violationName;
|
|
187
|
+
message = problem.violationMessage ?? problem.violationName;
|
|
188
|
+
stack = captureCallsiteStack();
|
|
189
|
+
kind = kindOverride ?? "violation";
|
|
190
|
+
} else if (problem instanceof Error) {
|
|
191
|
+
errorType = problem.name || "Error";
|
|
192
|
+
message = problem.message || String(problem);
|
|
193
|
+
stack = problem.stack ?? void 0;
|
|
194
|
+
kind = kindOverride ?? "caught";
|
|
195
|
+
} else {
|
|
196
|
+
errorType = "Error";
|
|
197
|
+
message = typeof problem === "string" ? problem : safeString(problem);
|
|
198
|
+
stack = captureCallsiteStack();
|
|
199
|
+
kind = kindOverride ?? "caught";
|
|
200
|
+
}
|
|
201
|
+
const ev = {
|
|
202
|
+
type: "error",
|
|
203
|
+
kind,
|
|
204
|
+
error_type: truncate(errorType, SEE_MAX_SUBJECT),
|
|
205
|
+
message: truncate(message, SEE_MAX_MESSAGE),
|
|
206
|
+
subject: consequence.subject,
|
|
207
|
+
outcome: consequence.outcome,
|
|
208
|
+
side: ctx.side,
|
|
209
|
+
sdk_version: ctx.sdkVersion,
|
|
210
|
+
ts: Date.now()
|
|
211
|
+
};
|
|
212
|
+
if (stack) ev.stack = truncate(stack, SEE_MAX_STACK);
|
|
213
|
+
const cleanExtras = sanitizeExtras(extras);
|
|
214
|
+
if (cleanExtras) ev.extras = cleanExtras;
|
|
215
|
+
if (ctx.url) ev.url = truncate(ctx.url, SEE_MAX_SUBJECT);
|
|
216
|
+
if (ctx.userId) ev.user_id = ctx.userId;
|
|
217
|
+
if (ctx.anonId) ev.anonymous_id = ctx.anonId;
|
|
218
|
+
if (ctx.env) ev.env = ctx.env;
|
|
219
|
+
return ev;
|
|
220
|
+
}
|
|
221
|
+
function safeString(v) {
|
|
222
|
+
try {
|
|
223
|
+
return typeof v === "object" ? JSON.stringify(v) : String(v);
|
|
224
|
+
} catch {
|
|
225
|
+
return String(v);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
var scheduleMicrotask = typeof queueMicrotask === "function" ? queueMicrotask : (cb) => {
|
|
229
|
+
void Promise.resolve().then(cb);
|
|
230
|
+
};
|
|
231
|
+
function startSeeChain(getProblem, dispatch) {
|
|
232
|
+
let subject;
|
|
233
|
+
let outcome;
|
|
234
|
+
let collected;
|
|
235
|
+
let flushed = false;
|
|
236
|
+
scheduleMicrotask(() => {
|
|
237
|
+
if (flushed) return;
|
|
238
|
+
flushed = true;
|
|
239
|
+
dispatch(
|
|
240
|
+
getProblem(),
|
|
241
|
+
causesThe(subject ?? "the app").to(outcome ?? "hit an error"),
|
|
242
|
+
collected
|
|
243
|
+
);
|
|
244
|
+
});
|
|
245
|
+
const tail = {
|
|
246
|
+
extras(x) {
|
|
247
|
+
if (x && typeof x === "object") collected = { ...collected, ...x };
|
|
248
|
+
return tail;
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
const step = {
|
|
252
|
+
to(o) {
|
|
253
|
+
outcome = String(o);
|
|
254
|
+
return tail;
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
const start = (s) => {
|
|
258
|
+
subject = String(s);
|
|
259
|
+
return step;
|
|
260
|
+
};
|
|
261
|
+
return { causes_the: start, causesThe: start };
|
|
262
|
+
}
|
|
263
|
+
function startSeeViolationChain(name, dispatch) {
|
|
264
|
+
let msg;
|
|
265
|
+
const base = startSeeChain(
|
|
266
|
+
() => msg !== void 0 ? violation(name).message(msg) : violation(name),
|
|
267
|
+
dispatch
|
|
268
|
+
);
|
|
269
|
+
const chain = {
|
|
270
|
+
...base,
|
|
271
|
+
message(m) {
|
|
272
|
+
msg = String(m);
|
|
273
|
+
return chain;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
return chain;
|
|
277
|
+
}
|
|
278
|
+
function topStackLine(stack) {
|
|
279
|
+
if (!stack) return "";
|
|
280
|
+
for (const line of stack.split("\n")) {
|
|
281
|
+
if (/^\s*at |@|:\d+:\d+/.test(line)) return line.trim().slice(0, 200);
|
|
282
|
+
}
|
|
283
|
+
return "";
|
|
284
|
+
}
|
|
285
|
+
var SeeLimiter = class {
|
|
286
|
+
constructor(maxPerSession = SEE_MAX_PER_SESSION, dedupWindowMs = SEE_DEDUP_WINDOW_MS) {
|
|
287
|
+
this.maxPerSession = maxPerSession;
|
|
288
|
+
this.dedupWindowMs = dedupWindowMs;
|
|
289
|
+
}
|
|
290
|
+
maxPerSession;
|
|
291
|
+
dedupWindowMs;
|
|
292
|
+
lastSent = /* @__PURE__ */ new Map();
|
|
293
|
+
sent = 0;
|
|
294
|
+
shouldSend(ev) {
|
|
295
|
+
if (this.sent >= this.maxPerSession) return false;
|
|
296
|
+
const key = `${ev.kind}|${ev.error_type}|${ev.message.slice(0, 200)}|${topStackLine(ev.stack)}`;
|
|
297
|
+
const now = Date.now();
|
|
298
|
+
const prev = this.lastSent.get(key);
|
|
299
|
+
if (prev !== void 0 && now - prev < this.dedupWindowMs) return false;
|
|
300
|
+
this.lastSent.set(key, now);
|
|
301
|
+
this.sent += 1;
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
105
306
|
// src/client/index.ts
|
|
106
|
-
var version = "
|
|
307
|
+
var version = "4.0.0";
|
|
107
308
|
var FLUSH_INTERVAL_MS = 5e3;
|
|
108
309
|
var MAX_BUFFER = 100;
|
|
109
310
|
var ANON_ID_KEY = "__se_anon_id";
|
|
@@ -137,6 +338,13 @@ var EventBuffer = class {
|
|
|
137
338
|
this.timer = null;
|
|
138
339
|
}
|
|
139
340
|
}
|
|
341
|
+
/** True once this visitor has been exposed to ≥1 experiment (this tab or a
|
|
342
|
+
* prior page in the session — the dedup set persists in sessionStorage).
|
|
343
|
+
* Gates auto-metric emission: vitals from non-participants are never read
|
|
344
|
+
* by the analysis pipeline and would be pure AE write cost (see cost.md). */
|
|
345
|
+
hasExposures() {
|
|
346
|
+
return this.exposureSeen.size > 0;
|
|
347
|
+
}
|
|
140
348
|
pushExposure(experiment, group, userId, anonId) {
|
|
141
349
|
const key = `${userId || anonId}:${experiment}`;
|
|
142
350
|
if (this.exposureSeen.has(key)) return;
|
|
@@ -202,16 +410,29 @@ var EventBuffer = class {
|
|
|
202
410
|
flush(useBeacon = false) {
|
|
203
411
|
if (!this.queue.length) return;
|
|
204
412
|
const batch = this.queue.splice(0);
|
|
205
|
-
|
|
413
|
+
this.send(batch, useBeacon);
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Bypass the 5s queue and ship events immediately — used by see() error
|
|
417
|
+
* reporting so occurrences land near-real-time and survive page unload.
|
|
418
|
+
* Beacon-first (fire-and-forget, unload-safe), keepalive fetch fallback.
|
|
419
|
+
*/
|
|
420
|
+
sendNow(events) {
|
|
421
|
+
this.send(events, true);
|
|
422
|
+
}
|
|
423
|
+
send(batch, useBeacon) {
|
|
206
424
|
if (useBeacon && typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
207
425
|
const beaconBody = JSON.stringify({ k: this.sdkKey, events: batch });
|
|
208
|
-
|
|
209
|
-
|
|
426
|
+
try {
|
|
427
|
+
if (navigator.sendBeacon(this.collectUrl, new Blob([beaconBody], { type: "text/plain" })))
|
|
428
|
+
return;
|
|
429
|
+
} catch {
|
|
430
|
+
}
|
|
210
431
|
}
|
|
211
432
|
fetch(this.collectUrl, {
|
|
212
433
|
method: "POST",
|
|
213
434
|
headers: { "X-SDK-Key": this.sdkKey, "Content-Type": "application/json" },
|
|
214
|
-
body,
|
|
435
|
+
body: JSON.stringify({ events: batch }),
|
|
215
436
|
keepalive: true
|
|
216
437
|
}).catch(() => {
|
|
217
438
|
});
|
|
@@ -228,14 +449,12 @@ var EventBuffer = class {
|
|
|
228
449
|
});
|
|
229
450
|
}
|
|
230
451
|
};
|
|
231
|
-
|
|
232
|
-
function installAutoGuardrails(buffer, userId, anonId, groups) {
|
|
452
|
+
function installAutoGuardrails(buffer, userId, anonId, groups, reportSee, ignoreUrlPrefixes, always = false) {
|
|
233
453
|
if (typeof window === "undefined" || typeof PerformanceObserver === "undefined") return;
|
|
454
|
+
const shouldEmit = () => always || buffer.hasExposures();
|
|
234
455
|
let lcp = null;
|
|
235
456
|
let inp = null;
|
|
236
457
|
let clsBad = false;
|
|
237
|
-
let jsErrorCount = 0;
|
|
238
|
-
let netErrorCount = 0;
|
|
239
458
|
let navTimingFlushed = false;
|
|
240
459
|
if (groups.vitals) {
|
|
241
460
|
try {
|
|
@@ -274,68 +493,71 @@ function installAutoGuardrails(buffer, userId, anonId, groups) {
|
|
|
274
493
|
if (groups.errors) {
|
|
275
494
|
const origOnError = window.onerror;
|
|
276
495
|
window.onerror = (msg, source, lineno, _colno, err) => {
|
|
277
|
-
if (
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
496
|
+
if (!isExpected(err)) {
|
|
497
|
+
const problem = err ?? (typeof msg === "string" && msg ? msg : "Unknown error");
|
|
498
|
+
reportSee(
|
|
499
|
+
problem,
|
|
500
|
+
causesThe("the page").to("hit an unhandled error"),
|
|
501
|
+
{
|
|
502
|
+
source: typeof source === "string" ? source : void 0,
|
|
503
|
+
line: lineno ?? void 0
|
|
504
|
+
},
|
|
505
|
+
"uncaught"
|
|
506
|
+
);
|
|
286
507
|
}
|
|
287
508
|
if (typeof origOnError === "function") return origOnError(msg, source, lineno, _colno, err);
|
|
288
509
|
return false;
|
|
289
510
|
};
|
|
290
511
|
window.addEventListener("unhandledrejection", (e) => {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
});
|
|
300
|
-
}
|
|
512
|
+
const reason = e.reason;
|
|
513
|
+
if (isExpected(reason)) return;
|
|
514
|
+
reportSee(
|
|
515
|
+
reason ?? "Unhandled promise rejection",
|
|
516
|
+
causesThe("the page").to("hit an unhandled promise rejection"),
|
|
517
|
+
void 0,
|
|
518
|
+
"unhandled_rejection"
|
|
519
|
+
);
|
|
301
520
|
});
|
|
302
521
|
const origFetch = window.fetch;
|
|
303
522
|
window.fetch = async function(...args) {
|
|
304
523
|
const startedAt = typeof performance !== "undefined" ? performance.now() : 0;
|
|
305
524
|
const url = typeof args[0] === "string" ? args[0] : args[0].toString();
|
|
525
|
+
const ignored = ignoreUrlPrefixes.some((p) => p && url.startsWith(p));
|
|
526
|
+
const bareUrl = url.split("?")[0].slice(0, 200);
|
|
306
527
|
let res;
|
|
307
528
|
try {
|
|
308
529
|
res = await origFetch.apply(this, args);
|
|
309
530
|
} catch (err) {
|
|
310
|
-
if (
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
});
|
|
531
|
+
if (!ignored && !isExpected(err)) {
|
|
532
|
+
reportSee(
|
|
533
|
+
violation("NetworkError").message(`request to ${bareUrl} failed`),
|
|
534
|
+
causesThe("a network request").to("fail without a response"),
|
|
535
|
+
{ status: 0, url: url.slice(0, 200) },
|
|
536
|
+
"network"
|
|
537
|
+
);
|
|
318
538
|
}
|
|
319
539
|
throw err;
|
|
320
540
|
}
|
|
321
|
-
if (res.status >= 500
|
|
322
|
-
netErrorCount += 1;
|
|
541
|
+
if (!ignored && res.status >= 500) {
|
|
323
542
|
const elapsed = typeof performance !== "undefined" ? performance.now() - startedAt : 0;
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
status: res.status,
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
});
|
|
543
|
+
reportSee(
|
|
544
|
+
violation("Http5xx").message(`request to ${bareUrl} returned ${res.status}`),
|
|
545
|
+
causesThe("a network request").to(`fail with HTTP ${res.status}`),
|
|
546
|
+
{ status: res.status, url: url.slice(0, 200), duration_ms: Math.round(elapsed) },
|
|
547
|
+
"network"
|
|
548
|
+
);
|
|
331
549
|
}
|
|
332
550
|
return res;
|
|
333
551
|
};
|
|
334
552
|
}
|
|
335
553
|
const flushNavTiming = () => {
|
|
336
554
|
if (navTimingFlushed) return;
|
|
555
|
+
if (!groups.vitals) {
|
|
556
|
+
navTimingFlushed = true;
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
if (!shouldEmit()) return;
|
|
337
560
|
navTimingFlushed = true;
|
|
338
|
-
if (!groups.vitals) return;
|
|
339
561
|
try {
|
|
340
562
|
const navList = performance.getEntriesByType("navigation");
|
|
341
563
|
const nav = navList[0];
|
|
@@ -370,7 +592,7 @@ function installAutoGuardrails(buffer, userId, anonId, groups) {
|
|
|
370
592
|
};
|
|
371
593
|
if (groups.engagement) {
|
|
372
594
|
try {
|
|
373
|
-
buffer.pushMetric("__auto_session_active", userId, anonId, { value: 1 });
|
|
595
|
+
if (shouldEmit()) buffer.pushMetric("__auto_session_active", userId, anonId, { value: 1 });
|
|
374
596
|
} catch {
|
|
375
597
|
}
|
|
376
598
|
let lastEmit = Date.now();
|
|
@@ -378,6 +600,7 @@ function installAutoGuardrails(buffer, userId, anonId, groups) {
|
|
|
378
600
|
document.addEventListener("visibilitychange", () => {
|
|
379
601
|
if (document.visibilityState !== "visible") return;
|
|
380
602
|
if (Date.now() - lastEmit < SESSION_GAP_MS) return;
|
|
603
|
+
if (!shouldEmit()) return;
|
|
381
604
|
try {
|
|
382
605
|
buffer.pushMetric("__auto_session_active", userId, anonId, { value: 1 });
|
|
383
606
|
lastEmit = Date.now();
|
|
@@ -400,7 +623,7 @@ function installAutoGuardrails(buffer, userId, anonId, groups) {
|
|
|
400
623
|
}
|
|
401
624
|
const flushOnHide = () => {
|
|
402
625
|
flushNavTiming();
|
|
403
|
-
if (groups.vitals) {
|
|
626
|
+
if (groups.vitals && shouldEmit()) {
|
|
404
627
|
if (lcp !== null) buffer.pushMetric("__auto_lcp", userId, anonId, { value: lcp });
|
|
405
628
|
if (inp !== null) buffer.pushMetric("__auto_inp", userId, anonId, { value: inp });
|
|
406
629
|
if (clsBad) buffer.pushMetric("__auto_cls_binary", userId, anonId, { value: 1 });
|
|
@@ -483,12 +706,14 @@ var FlagsClientBrowser = class {
|
|
|
483
706
|
baseUrl;
|
|
484
707
|
autoGuardrails;
|
|
485
708
|
autoGuardrailGroups;
|
|
709
|
+
autoCollectAlways;
|
|
486
710
|
env;
|
|
487
711
|
evalResult = null;
|
|
488
712
|
anonId;
|
|
489
713
|
userId = "";
|
|
490
714
|
buffer;
|
|
491
715
|
telemetry;
|
|
716
|
+
seeLimiter = new SeeLimiter();
|
|
492
717
|
guardrailsInstalled = false;
|
|
493
718
|
listeners = /* @__PURE__ */ new Set();
|
|
494
719
|
overrideListenerInstalled = false;
|
|
@@ -504,6 +729,7 @@ var FlagsClientBrowser = class {
|
|
|
504
729
|
this.baseUrl = (opts.baseUrl ?? "https://edge.shipeasy.dev").replace(/\/$/, "");
|
|
505
730
|
this.env = opts.env ?? "prod";
|
|
506
731
|
this.autoGuardrails = opts.autoGuardrails !== false;
|
|
732
|
+
this.autoCollectAlways = opts.autoCollectAlways === true;
|
|
507
733
|
const g = opts.autoGuardrailGroups ?? {};
|
|
508
734
|
this.autoGuardrailGroups = {
|
|
509
735
|
vitals: g.vitals ?? this.autoGuardrails,
|
|
@@ -548,10 +774,38 @@ var FlagsClientBrowser = class {
|
|
|
548
774
|
const anyGroupOn = this.autoGuardrailGroups.vitals || this.autoGuardrailGroups.errors || this.autoGuardrailGroups.engagement;
|
|
549
775
|
if (anyGroupOn && !this.guardrailsInstalled) {
|
|
550
776
|
this.guardrailsInstalled = true;
|
|
551
|
-
installAutoGuardrails(
|
|
777
|
+
installAutoGuardrails(
|
|
778
|
+
this.buffer,
|
|
779
|
+
this.userId,
|
|
780
|
+
this.anonId,
|
|
781
|
+
this.autoGuardrailGroups,
|
|
782
|
+
(problem, consequence, extras, kind) => this.reportError(problem, consequence, extras, kind),
|
|
783
|
+
[`${this.baseUrl}/`, DEFAULT_TELEMETRY_URL],
|
|
784
|
+
this.autoCollectAlways
|
|
785
|
+
);
|
|
552
786
|
}
|
|
553
787
|
this.notify();
|
|
554
788
|
}
|
|
789
|
+
/**
|
|
790
|
+
* Report a structured error into the errors primitive. Flushes immediately
|
|
791
|
+
* (beacon-first) — error occurrences are near-real-time, never queued behind
|
|
792
|
+
* the 5s metric batch. Spam-guarded by a 30s dedup window + per-session cap.
|
|
793
|
+
*/
|
|
794
|
+
reportError(problem, consequence, extras, kind) {
|
|
795
|
+
try {
|
|
796
|
+
const ev = buildSeeEvent(problem, consequence, extras, {
|
|
797
|
+
side: "client",
|
|
798
|
+
sdkVersion: version,
|
|
799
|
+
env: this.env,
|
|
800
|
+
url: typeof window !== "undefined" && window.location ? window.location.href : void 0,
|
|
801
|
+
userId: this.userId || void 0,
|
|
802
|
+
anonId: this.anonId
|
|
803
|
+
}, kind);
|
|
804
|
+
if (!this.seeLimiter.shouldSend(ev)) return;
|
|
805
|
+
this.buffer.sendNow([ev]);
|
|
806
|
+
} catch {
|
|
807
|
+
}
|
|
808
|
+
}
|
|
555
809
|
get ready() {
|
|
556
810
|
return this.evalResult !== null;
|
|
557
811
|
}
|
|
@@ -783,13 +1037,15 @@ var _client = null;
|
|
|
783
1037
|
function shipeasy(opts) {
|
|
784
1038
|
const ac = opts.autoCollect;
|
|
785
1039
|
const blanket = ac === false ? false : true;
|
|
786
|
-
const
|
|
1040
|
+
const acObj = ac && typeof ac === "object" ? ac : void 0;
|
|
1041
|
+
const groups = acObj ? { vitals: acObj.vitals, errors: acObj.errors, engagement: acObj.engagement } : void 0;
|
|
787
1042
|
const baseUrl = opts.baseUrl ?? "https://cdn.shipeasy.ai";
|
|
788
1043
|
const client = configureShipeasy({
|
|
789
1044
|
sdkKey: opts.clientKey,
|
|
790
1045
|
baseUrl,
|
|
791
1046
|
autoGuardrails: blanket,
|
|
792
1047
|
autoGuardrailGroups: groups,
|
|
1048
|
+
autoCollectAlways: acObj?.always === true,
|
|
793
1049
|
disableTelemetry: opts.disableTelemetry
|
|
794
1050
|
});
|
|
795
1051
|
injectI18nLoader(opts.clientKey, baseUrl, opts.i18nProfile);
|
|
@@ -951,6 +1207,20 @@ var flags = {
|
|
|
951
1207
|
return _client?.ready ?? false;
|
|
952
1208
|
}
|
|
953
1209
|
};
|
|
1210
|
+
function dispatchSee(problem, consequence, extras, kind) {
|
|
1211
|
+
if (!_client) {
|
|
1212
|
+
console.warn("[shipeasy] see() called before shipeasy({ clientKey }) \u2014 error dropped");
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
_client.reportError(problem, consequence, extras, kind);
|
|
1216
|
+
}
|
|
1217
|
+
var see = Object.assign(
|
|
1218
|
+
(problem) => startSeeChain(() => problem, dispatchSee),
|
|
1219
|
+
{
|
|
1220
|
+
Violation: (name) => startSeeViolationChain(name, dispatchSee),
|
|
1221
|
+
ControlFlowException: markExpected
|
|
1222
|
+
}
|
|
1223
|
+
);
|
|
954
1224
|
var LABEL_MARKER_START = "\uFFF9";
|
|
955
1225
|
var LABEL_MARKER_SEP = "\uFFFA";
|
|
956
1226
|
var LABEL_MARKER_END = "\uFFFB";
|
|
@@ -1203,6 +1473,7 @@ var i18n = {
|
|
|
1203
1473
|
readConfigOverride,
|
|
1204
1474
|
readExpOverride,
|
|
1205
1475
|
readGateOverride,
|
|
1476
|
+
see,
|
|
1206
1477
|
shipeasy,
|
|
1207
1478
|
version
|
|
1208
1479
|
});
|