@shipeasy/sdk 2.2.0 → 2.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/client/index.d.mts +27 -5
- package/dist/client/index.d.ts +27 -5
- package/dist/client/index.js +132 -108
- package/dist/client/index.mjs +133 -109
- package/package.json +1 -1
package/dist/client/index.d.mts
CHANGED
|
@@ -28,11 +28,23 @@ interface EvalResponse {
|
|
|
28
28
|
configs: Record<string, unknown>;
|
|
29
29
|
experiments: Record<string, EvalExpResult>;
|
|
30
30
|
}
|
|
31
|
+
interface AutoCollectGroups {
|
|
32
|
+
vitals: boolean;
|
|
33
|
+
errors: boolean;
|
|
34
|
+
engagement: boolean;
|
|
35
|
+
}
|
|
31
36
|
type FlagsClientBrowserEnv = "dev" | "staging" | "prod";
|
|
32
37
|
interface FlagsClientBrowserOptions {
|
|
33
38
|
sdkKey: string;
|
|
34
39
|
baseUrl?: string;
|
|
35
40
|
autoGuardrails?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Per-group enablement for auto-collected metrics. When set, overrides the
|
|
43
|
+
* blanket `autoGuardrails` flag for the specific groups listed. Any group
|
|
44
|
+
* not present in the object falls back to `autoGuardrails` (defaulting to
|
|
45
|
+
* true when `autoGuardrails` is true).
|
|
46
|
+
*/
|
|
47
|
+
autoGuardrailGroups?: Partial<AutoCollectGroups>;
|
|
36
48
|
/** Which published env to read values from. Defaults to "prod". */
|
|
37
49
|
env?: FlagsClientBrowserEnv;
|
|
38
50
|
}
|
|
@@ -40,6 +52,7 @@ declare class FlagsClientBrowser {
|
|
|
40
52
|
private readonly sdkKey;
|
|
41
53
|
private readonly baseUrl;
|
|
42
54
|
private readonly autoGuardrails;
|
|
55
|
+
private readonly autoGuardrailGroups;
|
|
43
56
|
private readonly env;
|
|
44
57
|
private evalResult;
|
|
45
58
|
private anonId;
|
|
@@ -125,11 +138,20 @@ interface ShipeasyClientConfig {
|
|
|
125
138
|
*/
|
|
126
139
|
autoIdentify?: boolean;
|
|
127
140
|
/**
|
|
128
|
-
* Capture web vitals (LCP, CLS, INP, TTFB, FCP)
|
|
129
|
-
*
|
|
130
|
-
*
|
|
141
|
+
* Capture web vitals (LCP, CLS, INP, TTFB, FCP, navigation timing), JS /
|
|
142
|
+
* network errors, and engagement signals (abandonment) as `__auto_*`
|
|
143
|
+
* metric events. Defaults to `true` — the worker bypasses event-catalog
|
|
144
|
+
* validation for `__auto_*` names so this is safe out of the box.
|
|
145
|
+
*
|
|
146
|
+
* Pass `false` to disable everything, or a per-group object to narrow:
|
|
147
|
+
*
|
|
148
|
+
* ```ts
|
|
149
|
+
* shipeasy({ apiKey, autoCollect: false }); // off
|
|
150
|
+
* shipeasy({ apiKey, autoCollect: { errors: false } }); // vitals + engagement only
|
|
151
|
+
* shipeasy({ apiKey }); // all groups on
|
|
152
|
+
* ```
|
|
131
153
|
*/
|
|
132
|
-
autoCollect?: boolean
|
|
154
|
+
autoCollect?: boolean | Partial<AutoCollectGroups>;
|
|
133
155
|
}
|
|
134
156
|
/**
|
|
135
157
|
* Initialise the ShipEasy client SDK and wire up lazy devtools.
|
|
@@ -237,4 +259,4 @@ interface I18nFacade {
|
|
|
237
259
|
}
|
|
238
260
|
declare const i18n: I18nFacade;
|
|
239
261
|
|
|
240
|
-
export { type BootstrapPayload, type ExperimentResult, FlagsClientBrowser, type FlagsClientBrowserEnv, type FlagsClientBrowserOptions, type I18nFacade, type I18nKey, type I18nRichComponents, type I18nString, type I18nTagRenderer, type I18nVariables, LABEL_MARKER_END, LABEL_MARKER_RE, LABEL_MARKER_SEP, LABEL_MARKER_START, type LabelAttrs, type ShipeasyClientConfig, type ShipeasySdkBridge, type User, _resetShipeasyForTests, attachDevtools, configureShipeasy, encodeLabelMarker, flags, getShipeasyClient, i18n, isDevtoolsRequested, labelAttrs, loadDevtools, readConfigOverride, readExpOverride, readGateOverride, shipeasy, version };
|
|
262
|
+
export { type AutoCollectGroups, type BootstrapPayload, type ExperimentResult, FlagsClientBrowser, type FlagsClientBrowserEnv, type FlagsClientBrowserOptions, type I18nFacade, type I18nKey, type I18nRichComponents, type I18nString, type I18nTagRenderer, type I18nVariables, LABEL_MARKER_END, LABEL_MARKER_RE, LABEL_MARKER_SEP, LABEL_MARKER_START, type LabelAttrs, type ShipeasyClientConfig, type ShipeasySdkBridge, type User, _resetShipeasyForTests, attachDevtools, configureShipeasy, encodeLabelMarker, flags, getShipeasyClient, i18n, isDevtoolsRequested, labelAttrs, loadDevtools, readConfigOverride, readExpOverride, readGateOverride, shipeasy, version };
|
package/dist/client/index.d.ts
CHANGED
|
@@ -28,11 +28,23 @@ interface EvalResponse {
|
|
|
28
28
|
configs: Record<string, unknown>;
|
|
29
29
|
experiments: Record<string, EvalExpResult>;
|
|
30
30
|
}
|
|
31
|
+
interface AutoCollectGroups {
|
|
32
|
+
vitals: boolean;
|
|
33
|
+
errors: boolean;
|
|
34
|
+
engagement: boolean;
|
|
35
|
+
}
|
|
31
36
|
type FlagsClientBrowserEnv = "dev" | "staging" | "prod";
|
|
32
37
|
interface FlagsClientBrowserOptions {
|
|
33
38
|
sdkKey: string;
|
|
34
39
|
baseUrl?: string;
|
|
35
40
|
autoGuardrails?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Per-group enablement for auto-collected metrics. When set, overrides the
|
|
43
|
+
* blanket `autoGuardrails` flag for the specific groups listed. Any group
|
|
44
|
+
* not present in the object falls back to `autoGuardrails` (defaulting to
|
|
45
|
+
* true when `autoGuardrails` is true).
|
|
46
|
+
*/
|
|
47
|
+
autoGuardrailGroups?: Partial<AutoCollectGroups>;
|
|
36
48
|
/** Which published env to read values from. Defaults to "prod". */
|
|
37
49
|
env?: FlagsClientBrowserEnv;
|
|
38
50
|
}
|
|
@@ -40,6 +52,7 @@ declare class FlagsClientBrowser {
|
|
|
40
52
|
private readonly sdkKey;
|
|
41
53
|
private readonly baseUrl;
|
|
42
54
|
private readonly autoGuardrails;
|
|
55
|
+
private readonly autoGuardrailGroups;
|
|
43
56
|
private readonly env;
|
|
44
57
|
private evalResult;
|
|
45
58
|
private anonId;
|
|
@@ -125,11 +138,20 @@ interface ShipeasyClientConfig {
|
|
|
125
138
|
*/
|
|
126
139
|
autoIdentify?: boolean;
|
|
127
140
|
/**
|
|
128
|
-
* Capture web vitals (LCP, CLS, INP, TTFB, FCP)
|
|
129
|
-
*
|
|
130
|
-
*
|
|
141
|
+
* Capture web vitals (LCP, CLS, INP, TTFB, FCP, navigation timing), JS /
|
|
142
|
+
* network errors, and engagement signals (abandonment) as `__auto_*`
|
|
143
|
+
* metric events. Defaults to `true` — the worker bypasses event-catalog
|
|
144
|
+
* validation for `__auto_*` names so this is safe out of the box.
|
|
145
|
+
*
|
|
146
|
+
* Pass `false` to disable everything, or a per-group object to narrow:
|
|
147
|
+
*
|
|
148
|
+
* ```ts
|
|
149
|
+
* shipeasy({ apiKey, autoCollect: false }); // off
|
|
150
|
+
* shipeasy({ apiKey, autoCollect: { errors: false } }); // vitals + engagement only
|
|
151
|
+
* shipeasy({ apiKey }); // all groups on
|
|
152
|
+
* ```
|
|
131
153
|
*/
|
|
132
|
-
autoCollect?: boolean
|
|
154
|
+
autoCollect?: boolean | Partial<AutoCollectGroups>;
|
|
133
155
|
}
|
|
134
156
|
/**
|
|
135
157
|
* Initialise the ShipEasy client SDK and wire up lazy devtools.
|
|
@@ -237,4 +259,4 @@ interface I18nFacade {
|
|
|
237
259
|
}
|
|
238
260
|
declare const i18n: I18nFacade;
|
|
239
261
|
|
|
240
|
-
export { type BootstrapPayload, type ExperimentResult, FlagsClientBrowser, type FlagsClientBrowserEnv, type FlagsClientBrowserOptions, type I18nFacade, type I18nKey, type I18nRichComponents, type I18nString, type I18nTagRenderer, type I18nVariables, LABEL_MARKER_END, LABEL_MARKER_RE, LABEL_MARKER_SEP, LABEL_MARKER_START, type LabelAttrs, type ShipeasyClientConfig, type ShipeasySdkBridge, type User, _resetShipeasyForTests, attachDevtools, configureShipeasy, encodeLabelMarker, flags, getShipeasyClient, i18n, isDevtoolsRequested, labelAttrs, loadDevtools, readConfigOverride, readExpOverride, readGateOverride, shipeasy, version };
|
|
262
|
+
export { type AutoCollectGroups, type BootstrapPayload, type ExperimentResult, FlagsClientBrowser, type FlagsClientBrowserEnv, type FlagsClientBrowserOptions, type I18nFacade, type I18nKey, type I18nRichComponents, type I18nString, type I18nTagRenderer, type I18nVariables, LABEL_MARKER_END, LABEL_MARKER_RE, LABEL_MARKER_SEP, LABEL_MARKER_START, type LabelAttrs, type ShipeasyClientConfig, type ShipeasySdkBridge, type User, _resetShipeasyForTests, attachDevtools, configureShipeasy, encodeLabelMarker, flags, getShipeasyClient, i18n, isDevtoolsRequested, labelAttrs, loadDevtools, readConfigOverride, readExpOverride, readGateOverride, shipeasy, version };
|
package/dist/client/index.js
CHANGED
|
@@ -167,7 +167,7 @@ var EventBuffer = class {
|
|
|
167
167
|
}
|
|
168
168
|
};
|
|
169
169
|
var MAX_ERRORS_PER_SESSION = 5;
|
|
170
|
-
function installAutoGuardrails(buffer, userId, anonId) {
|
|
170
|
+
function installAutoGuardrails(buffer, userId, anonId, groups) {
|
|
171
171
|
if (typeof window === "undefined" || typeof PerformanceObserver === "undefined") return;
|
|
172
172
|
let lcp = null;
|
|
173
173
|
let inp = null;
|
|
@@ -175,100 +175,105 @@ function installAutoGuardrails(buffer, userId, anonId) {
|
|
|
175
175
|
let jsErrorCount = 0;
|
|
176
176
|
let netErrorCount = 0;
|
|
177
177
|
let navTimingFlushed = false;
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
lcpObs.observe({ type: "largest-contentful-paint", buffered: true });
|
|
185
|
-
} catch {
|
|
186
|
-
}
|
|
187
|
-
try {
|
|
188
|
-
const inpObs = new PerformanceObserver((list) => {
|
|
189
|
-
for (const e of list.getEntries()) {
|
|
190
|
-
const dur = e.duration ?? 0;
|
|
191
|
-
if (inp === null || dur > inp) inp = dur;
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
inpObs.observe({
|
|
195
|
-
type: "event",
|
|
196
|
-
buffered: true,
|
|
197
|
-
durationThreshold: 16
|
|
198
|
-
});
|
|
199
|
-
} catch {
|
|
200
|
-
}
|
|
201
|
-
try {
|
|
202
|
-
const clsObs = new PerformanceObserver((list) => {
|
|
203
|
-
for (const e of list.getEntries()) {
|
|
204
|
-
if (e.value > 0.1) clsBad = true;
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
clsObs.observe({ type: "layout-shift", buffered: true });
|
|
208
|
-
} catch {
|
|
209
|
-
}
|
|
210
|
-
const origOnError = window.onerror;
|
|
211
|
-
window.onerror = (msg, source, lineno, _colno, err) => {
|
|
212
|
-
if (jsErrorCount < MAX_ERRORS_PER_SESSION) {
|
|
213
|
-
jsErrorCount += 1;
|
|
214
|
-
buffer.pushMetric("__auto_js_error", userId, anonId, {
|
|
215
|
-
value: 1,
|
|
216
|
-
kind: "exception",
|
|
217
|
-
message: typeof msg === "string" ? msg.slice(0, 200) : String(err ?? "").slice(0, 200),
|
|
218
|
-
source: typeof source === "string" ? source.slice(0, 200) : "",
|
|
219
|
-
line: lineno ?? 0
|
|
178
|
+
if (groups.vitals) {
|
|
179
|
+
try {
|
|
180
|
+
const lcpObs = new PerformanceObserver((list) => {
|
|
181
|
+
const entries = list.getEntries();
|
|
182
|
+
if (entries.length)
|
|
183
|
+
lcp = entries[entries.length - 1].startTime;
|
|
220
184
|
});
|
|
185
|
+
lcpObs.observe({ type: "largest-contentful-paint", buffered: true });
|
|
186
|
+
} catch {
|
|
221
187
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const reason = e.reason;
|
|
229
|
-
const message = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : String(reason);
|
|
230
|
-
buffer.pushMetric("__auto_js_error", userId, anonId, {
|
|
231
|
-
value: 1,
|
|
232
|
-
kind: "unhandled_rejection",
|
|
233
|
-
message: message.slice(0, 200)
|
|
188
|
+
try {
|
|
189
|
+
const inpObs = new PerformanceObserver((list) => {
|
|
190
|
+
for (const e of list.getEntries()) {
|
|
191
|
+
const dur = e.duration ?? 0;
|
|
192
|
+
if (inp === null || dur > inp) inp = dur;
|
|
193
|
+
}
|
|
234
194
|
});
|
|
195
|
+
inpObs.observe({
|
|
196
|
+
type: "event",
|
|
197
|
+
buffered: true,
|
|
198
|
+
durationThreshold: 16
|
|
199
|
+
});
|
|
200
|
+
} catch {
|
|
235
201
|
}
|
|
236
|
-
});
|
|
237
|
-
const origFetch = window.fetch;
|
|
238
|
-
window.fetch = async function(...args) {
|
|
239
|
-
const startedAt = typeof performance !== "undefined" ? performance.now() : 0;
|
|
240
|
-
const url = typeof args[0] === "string" ? args[0] : args[0].toString();
|
|
241
|
-
let res;
|
|
242
202
|
try {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
203
|
+
const clsObs = new PerformanceObserver((list) => {
|
|
204
|
+
for (const e of list.getEntries()) {
|
|
205
|
+
if (e.value > 0.1) clsBad = true;
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
clsObs.observe({ type: "layout-shift", buffered: true });
|
|
209
|
+
} catch {
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (groups.errors) {
|
|
213
|
+
const origOnError = window.onerror;
|
|
214
|
+
window.onerror = (msg, source, lineno, _colno, err) => {
|
|
215
|
+
if (jsErrorCount < MAX_ERRORS_PER_SESSION) {
|
|
216
|
+
jsErrorCount += 1;
|
|
217
|
+
buffer.pushMetric("__auto_js_error", userId, anonId, {
|
|
218
|
+
value: 1,
|
|
219
|
+
kind: "exception",
|
|
220
|
+
message: typeof msg === "string" ? msg.slice(0, 200) : String(err ?? "").slice(0, 200),
|
|
221
|
+
source: typeof source === "string" ? source.slice(0, 200) : "",
|
|
222
|
+
line: lineno ?? 0
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
if (typeof origOnError === "function") return origOnError(msg, source, lineno, _colno, err);
|
|
226
|
+
return false;
|
|
227
|
+
};
|
|
228
|
+
window.addEventListener("unhandledrejection", (e) => {
|
|
229
|
+
if (jsErrorCount < MAX_ERRORS_PER_SESSION) {
|
|
230
|
+
jsErrorCount += 1;
|
|
231
|
+
const reason = e.reason;
|
|
232
|
+
const message = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : String(reason);
|
|
233
|
+
buffer.pushMetric("__auto_js_error", userId, anonId, {
|
|
234
|
+
value: 1,
|
|
235
|
+
kind: "unhandled_rejection",
|
|
236
|
+
message: message.slice(0, 200)
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
const origFetch = window.fetch;
|
|
241
|
+
window.fetch = async function(...args) {
|
|
242
|
+
const startedAt = typeof performance !== "undefined" ? performance.now() : 0;
|
|
243
|
+
const url = typeof args[0] === "string" ? args[0] : args[0].toString();
|
|
244
|
+
let res;
|
|
245
|
+
try {
|
|
246
|
+
res = await origFetch.apply(this, args);
|
|
247
|
+
} catch (err) {
|
|
248
|
+
if (netErrorCount < MAX_ERRORS_PER_SESSION) {
|
|
249
|
+
netErrorCount += 1;
|
|
250
|
+
buffer.pushMetric("__auto_network_error", userId, anonId, {
|
|
251
|
+
value: 1,
|
|
252
|
+
kind: "network",
|
|
253
|
+
status: 0,
|
|
254
|
+
url: url.slice(0, 200)
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
throw err;
|
|
258
|
+
}
|
|
259
|
+
if (res.status >= 500 && netErrorCount < MAX_ERRORS_PER_SESSION) {
|
|
246
260
|
netErrorCount += 1;
|
|
261
|
+
const elapsed = typeof performance !== "undefined" ? performance.now() - startedAt : 0;
|
|
247
262
|
buffer.pushMetric("__auto_network_error", userId, anonId, {
|
|
248
263
|
value: 1,
|
|
249
|
-
kind: "
|
|
250
|
-
status:
|
|
251
|
-
url: url.slice(0, 200)
|
|
264
|
+
kind: "5xx",
|
|
265
|
+
status: res.status,
|
|
266
|
+
url: url.slice(0, 200),
|
|
267
|
+
duration_ms: Math.round(elapsed)
|
|
252
268
|
});
|
|
253
269
|
}
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
netErrorCount += 1;
|
|
258
|
-
const elapsed = typeof performance !== "undefined" ? performance.now() - startedAt : 0;
|
|
259
|
-
buffer.pushMetric("__auto_network_error", userId, anonId, {
|
|
260
|
-
value: 1,
|
|
261
|
-
kind: "5xx",
|
|
262
|
-
status: res.status,
|
|
263
|
-
url: url.slice(0, 200),
|
|
264
|
-
duration_ms: Math.round(elapsed)
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
return res;
|
|
268
|
-
};
|
|
270
|
+
return res;
|
|
271
|
+
};
|
|
272
|
+
}
|
|
269
273
|
const flushNavTiming = () => {
|
|
270
274
|
if (navTimingFlushed) return;
|
|
271
275
|
navTimingFlushed = true;
|
|
276
|
+
if (!groups.vitals) return;
|
|
272
277
|
try {
|
|
273
278
|
const navList = performance.getEntriesByType("navigation");
|
|
274
279
|
const nav = navList[0];
|
|
@@ -301,29 +306,36 @@ function installAutoGuardrails(buffer, userId, anonId) {
|
|
|
301
306
|
} catch {
|
|
302
307
|
}
|
|
303
308
|
};
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
(
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
309
|
+
const needHide = groups.vitals || groups.engagement;
|
|
310
|
+
if (needHide) {
|
|
311
|
+
if (document.readyState === "complete") {
|
|
312
|
+
setTimeout(flushNavTiming, 0);
|
|
313
|
+
} else {
|
|
314
|
+
window.addEventListener(
|
|
315
|
+
"load",
|
|
316
|
+
() => {
|
|
317
|
+
setTimeout(flushNavTiming, 0);
|
|
318
|
+
},
|
|
319
|
+
{ once: true }
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
const flushOnHide = () => {
|
|
323
|
+
flushNavTiming();
|
|
324
|
+
if (groups.vitals) {
|
|
325
|
+
if (lcp !== null) buffer.pushMetric("__auto_lcp", userId, anonId, { value: lcp });
|
|
326
|
+
if (inp !== null) buffer.pushMetric("__auto_inp", userId, anonId, { value: inp });
|
|
327
|
+
if (clsBad) buffer.pushMetric("__auto_cls_binary", userId, anonId, { value: 1 });
|
|
328
|
+
}
|
|
329
|
+
if (groups.engagement) {
|
|
330
|
+
const abandoned = lcp === null ? 1 : 0;
|
|
331
|
+
buffer.pushMetric("__auto_abandoned", userId, anonId, { value: abandoned });
|
|
332
|
+
}
|
|
333
|
+
buffer.flush(true);
|
|
334
|
+
};
|
|
335
|
+
document.addEventListener("visibilitychange", () => {
|
|
336
|
+
if (document.visibilityState === "hidden") flushOnHide();
|
|
337
|
+
});
|
|
314
338
|
}
|
|
315
|
-
const flushOnHide = () => {
|
|
316
|
-
flushNavTiming();
|
|
317
|
-
if (lcp !== null) buffer.pushMetric("__auto_lcp", userId, anonId, { value: lcp });
|
|
318
|
-
if (inp !== null) buffer.pushMetric("__auto_inp", userId, anonId, { value: inp });
|
|
319
|
-
if (clsBad) buffer.pushMetric("__auto_cls_binary", userId, anonId, { value: 1 });
|
|
320
|
-
const abandoned = lcp === null ? 1 : 0;
|
|
321
|
-
buffer.pushMetric("__auto_abandoned", userId, anonId, { value: abandoned });
|
|
322
|
-
buffer.flush(true);
|
|
323
|
-
};
|
|
324
|
-
document.addEventListener("visibilitychange", () => {
|
|
325
|
-
if (document.visibilityState === "hidden") flushOnHide();
|
|
326
|
-
});
|
|
327
339
|
}
|
|
328
340
|
function getOrCreateAnonId() {
|
|
329
341
|
try {
|
|
@@ -391,6 +403,7 @@ var FlagsClientBrowser = class {
|
|
|
391
403
|
sdkKey;
|
|
392
404
|
baseUrl;
|
|
393
405
|
autoGuardrails;
|
|
406
|
+
autoGuardrailGroups;
|
|
394
407
|
env;
|
|
395
408
|
evalResult = null;
|
|
396
409
|
anonId;
|
|
@@ -410,7 +423,13 @@ var FlagsClientBrowser = class {
|
|
|
410
423
|
this.sdkKey = opts.sdkKey;
|
|
411
424
|
this.baseUrl = (opts.baseUrl ?? "https://edge.shipeasy.dev").replace(/\/$/, "");
|
|
412
425
|
this.env = opts.env ?? "prod";
|
|
413
|
-
this.autoGuardrails = opts.autoGuardrails
|
|
426
|
+
this.autoGuardrails = opts.autoGuardrails !== false;
|
|
427
|
+
const g = opts.autoGuardrailGroups ?? {};
|
|
428
|
+
this.autoGuardrailGroups = {
|
|
429
|
+
vitals: g.vitals ?? this.autoGuardrails,
|
|
430
|
+
errors: g.errors ?? this.autoGuardrails,
|
|
431
|
+
engagement: g.engagement ?? this.autoGuardrails
|
|
432
|
+
};
|
|
414
433
|
this.anonId = getOrCreateAnonId();
|
|
415
434
|
this.buffer = new EventBuffer(`${this.baseUrl}/collect`, this.sdkKey);
|
|
416
435
|
void this.buffer.flushPendingAlias();
|
|
@@ -439,9 +458,10 @@ var FlagsClientBrowser = class {
|
|
|
439
458
|
const data = await res.json();
|
|
440
459
|
if (seq !== this.identifySeq) return;
|
|
441
460
|
this.evalResult = data;
|
|
442
|
-
|
|
461
|
+
const anyGroupOn = this.autoGuardrailGroups.vitals || this.autoGuardrailGroups.errors || this.autoGuardrailGroups.engagement;
|
|
462
|
+
if (anyGroupOn && !this.guardrailsInstalled) {
|
|
443
463
|
this.guardrailsInstalled = true;
|
|
444
|
-
installAutoGuardrails(this.buffer, this.userId, this.anonId);
|
|
464
|
+
installAutoGuardrails(this.buffer, this.userId, this.anonId, this.autoGuardrailGroups);
|
|
445
465
|
}
|
|
446
466
|
this.notify();
|
|
447
467
|
}
|
|
@@ -657,10 +677,14 @@ function attachDevtools(client, opts = {}) {
|
|
|
657
677
|
}
|
|
658
678
|
var _client = null;
|
|
659
679
|
function shipeasy(opts) {
|
|
680
|
+
const ac = opts.autoCollect;
|
|
681
|
+
const blanket = ac === false ? false : true;
|
|
682
|
+
const groups = ac && typeof ac === "object" ? ac : void 0;
|
|
660
683
|
const client = configureShipeasy({
|
|
661
684
|
sdkKey: opts.apiKey,
|
|
662
685
|
baseUrl: opts.baseUrl ?? "https://cdn.shipeasy.ai",
|
|
663
|
-
autoGuardrails:
|
|
686
|
+
autoGuardrails: blanket,
|
|
687
|
+
autoGuardrailGroups: groups
|
|
664
688
|
});
|
|
665
689
|
flags.notifyMounted();
|
|
666
690
|
if (opts.autoIdentify !== false) {
|
package/dist/client/index.mjs
CHANGED
|
@@ -124,7 +124,7 @@ var EventBuffer = class {
|
|
|
124
124
|
}
|
|
125
125
|
};
|
|
126
126
|
var MAX_ERRORS_PER_SESSION = 5;
|
|
127
|
-
function installAutoGuardrails(buffer, userId, anonId) {
|
|
127
|
+
function installAutoGuardrails(buffer, userId, anonId, groups) {
|
|
128
128
|
if (typeof window === "undefined" || typeof PerformanceObserver === "undefined") return;
|
|
129
129
|
let lcp = null;
|
|
130
130
|
let inp = null;
|
|
@@ -132,100 +132,105 @@ function installAutoGuardrails(buffer, userId, anonId) {
|
|
|
132
132
|
let jsErrorCount = 0;
|
|
133
133
|
let netErrorCount = 0;
|
|
134
134
|
let navTimingFlushed = false;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
lcpObs.observe({ type: "largest-contentful-paint", buffered: true });
|
|
142
|
-
} catch {
|
|
143
|
-
}
|
|
144
|
-
try {
|
|
145
|
-
const inpObs = new PerformanceObserver((list) => {
|
|
146
|
-
for (const e of list.getEntries()) {
|
|
147
|
-
const dur = e.duration ?? 0;
|
|
148
|
-
if (inp === null || dur > inp) inp = dur;
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
inpObs.observe({
|
|
152
|
-
type: "event",
|
|
153
|
-
buffered: true,
|
|
154
|
-
durationThreshold: 16
|
|
155
|
-
});
|
|
156
|
-
} catch {
|
|
157
|
-
}
|
|
158
|
-
try {
|
|
159
|
-
const clsObs = new PerformanceObserver((list) => {
|
|
160
|
-
for (const e of list.getEntries()) {
|
|
161
|
-
if (e.value > 0.1) clsBad = true;
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
clsObs.observe({ type: "layout-shift", buffered: true });
|
|
165
|
-
} catch {
|
|
166
|
-
}
|
|
167
|
-
const origOnError = window.onerror;
|
|
168
|
-
window.onerror = (msg, source, lineno, _colno, err) => {
|
|
169
|
-
if (jsErrorCount < MAX_ERRORS_PER_SESSION) {
|
|
170
|
-
jsErrorCount += 1;
|
|
171
|
-
buffer.pushMetric("__auto_js_error", userId, anonId, {
|
|
172
|
-
value: 1,
|
|
173
|
-
kind: "exception",
|
|
174
|
-
message: typeof msg === "string" ? msg.slice(0, 200) : String(err ?? "").slice(0, 200),
|
|
175
|
-
source: typeof source === "string" ? source.slice(0, 200) : "",
|
|
176
|
-
line: lineno ?? 0
|
|
135
|
+
if (groups.vitals) {
|
|
136
|
+
try {
|
|
137
|
+
const lcpObs = new PerformanceObserver((list) => {
|
|
138
|
+
const entries = list.getEntries();
|
|
139
|
+
if (entries.length)
|
|
140
|
+
lcp = entries[entries.length - 1].startTime;
|
|
177
141
|
});
|
|
142
|
+
lcpObs.observe({ type: "largest-contentful-paint", buffered: true });
|
|
143
|
+
} catch {
|
|
178
144
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const reason = e.reason;
|
|
186
|
-
const message = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : String(reason);
|
|
187
|
-
buffer.pushMetric("__auto_js_error", userId, anonId, {
|
|
188
|
-
value: 1,
|
|
189
|
-
kind: "unhandled_rejection",
|
|
190
|
-
message: message.slice(0, 200)
|
|
145
|
+
try {
|
|
146
|
+
const inpObs = new PerformanceObserver((list) => {
|
|
147
|
+
for (const e of list.getEntries()) {
|
|
148
|
+
const dur = e.duration ?? 0;
|
|
149
|
+
if (inp === null || dur > inp) inp = dur;
|
|
150
|
+
}
|
|
191
151
|
});
|
|
152
|
+
inpObs.observe({
|
|
153
|
+
type: "event",
|
|
154
|
+
buffered: true,
|
|
155
|
+
durationThreshold: 16
|
|
156
|
+
});
|
|
157
|
+
} catch {
|
|
192
158
|
}
|
|
193
|
-
});
|
|
194
|
-
const origFetch = window.fetch;
|
|
195
|
-
window.fetch = async function(...args) {
|
|
196
|
-
const startedAt = typeof performance !== "undefined" ? performance.now() : 0;
|
|
197
|
-
const url = typeof args[0] === "string" ? args[0] : args[0].toString();
|
|
198
|
-
let res;
|
|
199
159
|
try {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
160
|
+
const clsObs = new PerformanceObserver((list) => {
|
|
161
|
+
for (const e of list.getEntries()) {
|
|
162
|
+
if (e.value > 0.1) clsBad = true;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
clsObs.observe({ type: "layout-shift", buffered: true });
|
|
166
|
+
} catch {
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (groups.errors) {
|
|
170
|
+
const origOnError = window.onerror;
|
|
171
|
+
window.onerror = (msg, source, lineno, _colno, err) => {
|
|
172
|
+
if (jsErrorCount < MAX_ERRORS_PER_SESSION) {
|
|
173
|
+
jsErrorCount += 1;
|
|
174
|
+
buffer.pushMetric("__auto_js_error", userId, anonId, {
|
|
175
|
+
value: 1,
|
|
176
|
+
kind: "exception",
|
|
177
|
+
message: typeof msg === "string" ? msg.slice(0, 200) : String(err ?? "").slice(0, 200),
|
|
178
|
+
source: typeof source === "string" ? source.slice(0, 200) : "",
|
|
179
|
+
line: lineno ?? 0
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
if (typeof origOnError === "function") return origOnError(msg, source, lineno, _colno, err);
|
|
183
|
+
return false;
|
|
184
|
+
};
|
|
185
|
+
window.addEventListener("unhandledrejection", (e) => {
|
|
186
|
+
if (jsErrorCount < MAX_ERRORS_PER_SESSION) {
|
|
187
|
+
jsErrorCount += 1;
|
|
188
|
+
const reason = e.reason;
|
|
189
|
+
const message = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : String(reason);
|
|
190
|
+
buffer.pushMetric("__auto_js_error", userId, anonId, {
|
|
191
|
+
value: 1,
|
|
192
|
+
kind: "unhandled_rejection",
|
|
193
|
+
message: message.slice(0, 200)
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
const origFetch = window.fetch;
|
|
198
|
+
window.fetch = async function(...args) {
|
|
199
|
+
const startedAt = typeof performance !== "undefined" ? performance.now() : 0;
|
|
200
|
+
const url = typeof args[0] === "string" ? args[0] : args[0].toString();
|
|
201
|
+
let res;
|
|
202
|
+
try {
|
|
203
|
+
res = await origFetch.apply(this, args);
|
|
204
|
+
} catch (err) {
|
|
205
|
+
if (netErrorCount < MAX_ERRORS_PER_SESSION) {
|
|
206
|
+
netErrorCount += 1;
|
|
207
|
+
buffer.pushMetric("__auto_network_error", userId, anonId, {
|
|
208
|
+
value: 1,
|
|
209
|
+
kind: "network",
|
|
210
|
+
status: 0,
|
|
211
|
+
url: url.slice(0, 200)
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
throw err;
|
|
215
|
+
}
|
|
216
|
+
if (res.status >= 500 && netErrorCount < MAX_ERRORS_PER_SESSION) {
|
|
203
217
|
netErrorCount += 1;
|
|
218
|
+
const elapsed = typeof performance !== "undefined" ? performance.now() - startedAt : 0;
|
|
204
219
|
buffer.pushMetric("__auto_network_error", userId, anonId, {
|
|
205
220
|
value: 1,
|
|
206
|
-
kind: "
|
|
207
|
-
status:
|
|
208
|
-
url: url.slice(0, 200)
|
|
221
|
+
kind: "5xx",
|
|
222
|
+
status: res.status,
|
|
223
|
+
url: url.slice(0, 200),
|
|
224
|
+
duration_ms: Math.round(elapsed)
|
|
209
225
|
});
|
|
210
226
|
}
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
netErrorCount += 1;
|
|
215
|
-
const elapsed = typeof performance !== "undefined" ? performance.now() - startedAt : 0;
|
|
216
|
-
buffer.pushMetric("__auto_network_error", userId, anonId, {
|
|
217
|
-
value: 1,
|
|
218
|
-
kind: "5xx",
|
|
219
|
-
status: res.status,
|
|
220
|
-
url: url.slice(0, 200),
|
|
221
|
-
duration_ms: Math.round(elapsed)
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
return res;
|
|
225
|
-
};
|
|
227
|
+
return res;
|
|
228
|
+
};
|
|
229
|
+
}
|
|
226
230
|
const flushNavTiming = () => {
|
|
227
231
|
if (navTimingFlushed) return;
|
|
228
232
|
navTimingFlushed = true;
|
|
233
|
+
if (!groups.vitals) return;
|
|
229
234
|
try {
|
|
230
235
|
const navList = performance.getEntriesByType("navigation");
|
|
231
236
|
const nav = navList[0];
|
|
@@ -258,29 +263,36 @@ function installAutoGuardrails(buffer, userId, anonId) {
|
|
|
258
263
|
} catch {
|
|
259
264
|
}
|
|
260
265
|
};
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
(
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
266
|
+
const needHide = groups.vitals || groups.engagement;
|
|
267
|
+
if (needHide) {
|
|
268
|
+
if (document.readyState === "complete") {
|
|
269
|
+
setTimeout(flushNavTiming, 0);
|
|
270
|
+
} else {
|
|
271
|
+
window.addEventListener(
|
|
272
|
+
"load",
|
|
273
|
+
() => {
|
|
274
|
+
setTimeout(flushNavTiming, 0);
|
|
275
|
+
},
|
|
276
|
+
{ once: true }
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
const flushOnHide = () => {
|
|
280
|
+
flushNavTiming();
|
|
281
|
+
if (groups.vitals) {
|
|
282
|
+
if (lcp !== null) buffer.pushMetric("__auto_lcp", userId, anonId, { value: lcp });
|
|
283
|
+
if (inp !== null) buffer.pushMetric("__auto_inp", userId, anonId, { value: inp });
|
|
284
|
+
if (clsBad) buffer.pushMetric("__auto_cls_binary", userId, anonId, { value: 1 });
|
|
285
|
+
}
|
|
286
|
+
if (groups.engagement) {
|
|
287
|
+
const abandoned = lcp === null ? 1 : 0;
|
|
288
|
+
buffer.pushMetric("__auto_abandoned", userId, anonId, { value: abandoned });
|
|
289
|
+
}
|
|
290
|
+
buffer.flush(true);
|
|
291
|
+
};
|
|
292
|
+
document.addEventListener("visibilitychange", () => {
|
|
293
|
+
if (document.visibilityState === "hidden") flushOnHide();
|
|
294
|
+
});
|
|
295
|
+
}
|
|
284
296
|
}
|
|
285
297
|
function getOrCreateAnonId() {
|
|
286
298
|
try {
|
|
@@ -348,6 +360,7 @@ var FlagsClientBrowser = class {
|
|
|
348
360
|
sdkKey;
|
|
349
361
|
baseUrl;
|
|
350
362
|
autoGuardrails;
|
|
363
|
+
autoGuardrailGroups;
|
|
351
364
|
env;
|
|
352
365
|
evalResult = null;
|
|
353
366
|
anonId;
|
|
@@ -367,7 +380,13 @@ var FlagsClientBrowser = class {
|
|
|
367
380
|
this.sdkKey = opts.sdkKey;
|
|
368
381
|
this.baseUrl = (opts.baseUrl ?? "https://edge.shipeasy.dev").replace(/\/$/, "");
|
|
369
382
|
this.env = opts.env ?? "prod";
|
|
370
|
-
this.autoGuardrails = opts.autoGuardrails
|
|
383
|
+
this.autoGuardrails = opts.autoGuardrails !== false;
|
|
384
|
+
const g = opts.autoGuardrailGroups ?? {};
|
|
385
|
+
this.autoGuardrailGroups = {
|
|
386
|
+
vitals: g.vitals ?? this.autoGuardrails,
|
|
387
|
+
errors: g.errors ?? this.autoGuardrails,
|
|
388
|
+
engagement: g.engagement ?? this.autoGuardrails
|
|
389
|
+
};
|
|
371
390
|
this.anonId = getOrCreateAnonId();
|
|
372
391
|
this.buffer = new EventBuffer(`${this.baseUrl}/collect`, this.sdkKey);
|
|
373
392
|
void this.buffer.flushPendingAlias();
|
|
@@ -396,9 +415,10 @@ var FlagsClientBrowser = class {
|
|
|
396
415
|
const data = await res.json();
|
|
397
416
|
if (seq !== this.identifySeq) return;
|
|
398
417
|
this.evalResult = data;
|
|
399
|
-
|
|
418
|
+
const anyGroupOn = this.autoGuardrailGroups.vitals || this.autoGuardrailGroups.errors || this.autoGuardrailGroups.engagement;
|
|
419
|
+
if (anyGroupOn && !this.guardrailsInstalled) {
|
|
400
420
|
this.guardrailsInstalled = true;
|
|
401
|
-
installAutoGuardrails(this.buffer, this.userId, this.anonId);
|
|
421
|
+
installAutoGuardrails(this.buffer, this.userId, this.anonId, this.autoGuardrailGroups);
|
|
402
422
|
}
|
|
403
423
|
this.notify();
|
|
404
424
|
}
|
|
@@ -614,10 +634,14 @@ function attachDevtools(client, opts = {}) {
|
|
|
614
634
|
}
|
|
615
635
|
var _client = null;
|
|
616
636
|
function shipeasy(opts) {
|
|
637
|
+
const ac = opts.autoCollect;
|
|
638
|
+
const blanket = ac === false ? false : true;
|
|
639
|
+
const groups = ac && typeof ac === "object" ? ac : void 0;
|
|
617
640
|
const client = configureShipeasy({
|
|
618
641
|
sdkKey: opts.apiKey,
|
|
619
642
|
baseUrl: opts.baseUrl ?? "https://cdn.shipeasy.ai",
|
|
620
|
-
autoGuardrails:
|
|
643
|
+
autoGuardrails: blanket,
|
|
644
|
+
autoGuardrailGroups: groups
|
|
621
645
|
});
|
|
622
646
|
flags.notifyMounted();
|
|
623
647
|
if (opts.autoIdentify !== false) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipeasy/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Shipeasy SDK — feature gates, runtime configs, experiments, and metrics for the Shipeasy hosted service.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"homepage": "https://shipeasy.ai",
|