@shipeasy/sdk 4.2.0 → 4.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 +21 -2
- package/dist/client/index.d.ts +21 -2
- package/dist/client/index.js +44 -7
- package/dist/client/index.mjs +42 -7
- package/dist/server/index.d.mts +19 -1
- package/dist/server/index.d.ts +19 -1
- package/dist/server/index.js +15 -2
- package/dist/server/index.mjs +13 -2
- package/package.json +1 -1
package/dist/client/index.d.mts
CHANGED
|
@@ -51,6 +51,16 @@ interface SeeErrorEvent {
|
|
|
51
51
|
env?: string;
|
|
52
52
|
sdk_version: string;
|
|
53
53
|
ts: number;
|
|
54
|
+
/**
|
|
55
|
+
* Per-request correlation token. The client mints one per same-origin fetch
|
|
56
|
+
* and ships it on both the request header (`X-SE-Correlation`) and any 5xx
|
|
57
|
+
* occurrence it reports; the server safety net reports the matching uncaught
|
|
58
|
+
* error under the same token. The backend joins the two issues by it —
|
|
59
|
+
* populating `caused_by` across the network boundary, where the in-process
|
|
60
|
+
* `.cause`-chain stamp (see `findCausedBy`) cannot reach. Join-only metadata,
|
|
61
|
+
* never persisted as an issue field.
|
|
62
|
+
*/
|
|
63
|
+
correlation_id?: string;
|
|
54
64
|
/**
|
|
55
65
|
* The earlier reported problem this occurrence descends from — present when
|
|
56
66
|
* the same error was caught + reported at an inner boundary and then
|
|
@@ -120,6 +130,15 @@ interface AutoCollectGroups {
|
|
|
120
130
|
errors: boolean;
|
|
121
131
|
engagement: boolean;
|
|
122
132
|
}
|
|
133
|
+
/** True when `rawUrl` resolves to the page's own origin (relative URLs included). */
|
|
134
|
+
declare function sameOrigin(rawUrl: string): boolean;
|
|
135
|
+
/**
|
|
136
|
+
* Return a new `fetch` args tuple with `X-SE-Correlation` added, preserving any
|
|
137
|
+
* existing headers across all three arg shapes (string / URL / Request). Never
|
|
138
|
+
* mutates the caller's objects. Best-effort: on any failure the original args
|
|
139
|
+
* pass through unchanged (correlation is optional, never breaks the fetch).
|
|
140
|
+
*/
|
|
141
|
+
declare function injectCorrelationHeader(args: Parameters<typeof fetch>, corr: string): Parameters<typeof fetch>;
|
|
123
142
|
type FlagsClientBrowserEnv = "dev" | "staging" | "prod";
|
|
124
143
|
interface FlagsClientBrowserOptions {
|
|
125
144
|
sdkKey: string;
|
|
@@ -176,7 +195,7 @@ declare class FlagsClientBrowser {
|
|
|
176
195
|
* (beacon-first) — error occurrences are near-real-time, never queued behind
|
|
177
196
|
* the 5s metric batch. Spam-guarded by a 30s dedup window + per-session cap.
|
|
178
197
|
*/
|
|
179
|
-
reportError(problem: unknown, consequence: Consequence, extras?: SeeExtras, kind?: SeeKind): void;
|
|
198
|
+
reportError(problem: unknown, consequence: Consequence, extras?: SeeExtras, kind?: SeeKind, correlationId?: string): void;
|
|
180
199
|
get ready(): boolean;
|
|
181
200
|
private notify;
|
|
182
201
|
initFromBootstrap(data: EvalResponse): void;
|
|
@@ -478,4 +497,4 @@ interface I18nFacade {
|
|
|
478
497
|
}
|
|
479
498
|
declare const i18n: I18nFacade;
|
|
480
499
|
|
|
481
|
-
export { type AutoCollectGroups, type BootstrapPayload, type Consequence, 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 SeeApi, type SeeChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyClientConfig, type ShipeasySdkBridge, type User, type Violation, _resetShipeasyForTests, attachDevtools, configureShipeasy, encodeLabelMarker, flags, getShipeasyClient, i18n, isDevtoolsRequested, labelAttrs, loadDevtools, readConfigOverride, readExpOverride, readGateOverride, see, shipeasy, version };
|
|
500
|
+
export { type AutoCollectGroups, type BootstrapPayload, type Consequence, 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 SeeApi, type SeeChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyClientConfig, type ShipeasySdkBridge, type User, type Violation, _resetShipeasyForTests, attachDevtools, configureShipeasy, encodeLabelMarker, flags, getShipeasyClient, i18n, injectCorrelationHeader, isDevtoolsRequested, labelAttrs, loadDevtools, readConfigOverride, readExpOverride, readGateOverride, sameOrigin, see, shipeasy, version };
|
package/dist/client/index.d.ts
CHANGED
|
@@ -51,6 +51,16 @@ interface SeeErrorEvent {
|
|
|
51
51
|
env?: string;
|
|
52
52
|
sdk_version: string;
|
|
53
53
|
ts: number;
|
|
54
|
+
/**
|
|
55
|
+
* Per-request correlation token. The client mints one per same-origin fetch
|
|
56
|
+
* and ships it on both the request header (`X-SE-Correlation`) and any 5xx
|
|
57
|
+
* occurrence it reports; the server safety net reports the matching uncaught
|
|
58
|
+
* error under the same token. The backend joins the two issues by it —
|
|
59
|
+
* populating `caused_by` across the network boundary, where the in-process
|
|
60
|
+
* `.cause`-chain stamp (see `findCausedBy`) cannot reach. Join-only metadata,
|
|
61
|
+
* never persisted as an issue field.
|
|
62
|
+
*/
|
|
63
|
+
correlation_id?: string;
|
|
54
64
|
/**
|
|
55
65
|
* The earlier reported problem this occurrence descends from — present when
|
|
56
66
|
* the same error was caught + reported at an inner boundary and then
|
|
@@ -120,6 +130,15 @@ interface AutoCollectGroups {
|
|
|
120
130
|
errors: boolean;
|
|
121
131
|
engagement: boolean;
|
|
122
132
|
}
|
|
133
|
+
/** True when `rawUrl` resolves to the page's own origin (relative URLs included). */
|
|
134
|
+
declare function sameOrigin(rawUrl: string): boolean;
|
|
135
|
+
/**
|
|
136
|
+
* Return a new `fetch` args tuple with `X-SE-Correlation` added, preserving any
|
|
137
|
+
* existing headers across all three arg shapes (string / URL / Request). Never
|
|
138
|
+
* mutates the caller's objects. Best-effort: on any failure the original args
|
|
139
|
+
* pass through unchanged (correlation is optional, never breaks the fetch).
|
|
140
|
+
*/
|
|
141
|
+
declare function injectCorrelationHeader(args: Parameters<typeof fetch>, corr: string): Parameters<typeof fetch>;
|
|
123
142
|
type FlagsClientBrowserEnv = "dev" | "staging" | "prod";
|
|
124
143
|
interface FlagsClientBrowserOptions {
|
|
125
144
|
sdkKey: string;
|
|
@@ -176,7 +195,7 @@ declare class FlagsClientBrowser {
|
|
|
176
195
|
* (beacon-first) — error occurrences are near-real-time, never queued behind
|
|
177
196
|
* the 5s metric batch. Spam-guarded by a 30s dedup window + per-session cap.
|
|
178
197
|
*/
|
|
179
|
-
reportError(problem: unknown, consequence: Consequence, extras?: SeeExtras, kind?: SeeKind): void;
|
|
198
|
+
reportError(problem: unknown, consequence: Consequence, extras?: SeeExtras, kind?: SeeKind, correlationId?: string): void;
|
|
180
199
|
get ready(): boolean;
|
|
181
200
|
private notify;
|
|
182
201
|
initFromBootstrap(data: EvalResponse): void;
|
|
@@ -478,4 +497,4 @@ interface I18nFacade {
|
|
|
478
497
|
}
|
|
479
498
|
declare const i18n: I18nFacade;
|
|
480
499
|
|
|
481
|
-
export { type AutoCollectGroups, type BootstrapPayload, type Consequence, 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 SeeApi, type SeeChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyClientConfig, type ShipeasySdkBridge, type User, type Violation, _resetShipeasyForTests, attachDevtools, configureShipeasy, encodeLabelMarker, flags, getShipeasyClient, i18n, isDevtoolsRequested, labelAttrs, loadDevtools, readConfigOverride, readExpOverride, readGateOverride, see, shipeasy, version };
|
|
500
|
+
export { type AutoCollectGroups, type BootstrapPayload, type Consequence, 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 SeeApi, type SeeChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyClientConfig, type ShipeasySdkBridge, type User, type Violation, _resetShipeasyForTests, attachDevtools, configureShipeasy, encodeLabelMarker, flags, getShipeasyClient, i18n, injectCorrelationHeader, isDevtoolsRequested, labelAttrs, loadDevtools, readConfigOverride, readExpOverride, readGateOverride, sameOrigin, see, shipeasy, version };
|
package/dist/client/index.js
CHANGED
|
@@ -32,12 +32,14 @@ __export(client_exports, {
|
|
|
32
32
|
flags: () => flags,
|
|
33
33
|
getShipeasyClient: () => getShipeasyClient,
|
|
34
34
|
i18n: () => i18n,
|
|
35
|
+
injectCorrelationHeader: () => injectCorrelationHeader,
|
|
35
36
|
isDevtoolsRequested: () => isDevtoolsRequested,
|
|
36
37
|
labelAttrs: () => labelAttrs,
|
|
37
38
|
loadDevtools: () => loadDevtools,
|
|
38
39
|
readConfigOverride: () => readConfigOverride,
|
|
39
40
|
readExpOverride: () => readExpOverride,
|
|
40
41
|
readGateOverride: () => readGateOverride,
|
|
42
|
+
sameOrigin: () => sameOrigin,
|
|
41
43
|
see: () => see,
|
|
42
44
|
shipeasy: () => shipeasy,
|
|
43
45
|
version: () => version
|
|
@@ -109,6 +111,7 @@ var SEE_MAX_STACK = 8e3;
|
|
|
109
111
|
var SEE_MAX_SUBJECT = 200;
|
|
110
112
|
var SEE_MAX_EXTRA_VALUE = 200;
|
|
111
113
|
var SEE_MAX_EXTRA_KEYS = 20;
|
|
114
|
+
var SEE_MAX_CORRELATION = 64;
|
|
112
115
|
var SEE_DEDUP_WINDOW_MS = 3e4;
|
|
113
116
|
var SEE_MAX_PER_SESSION = 25;
|
|
114
117
|
function causesThe(subject) {
|
|
@@ -215,7 +218,7 @@ function captureCallsiteStack() {
|
|
|
215
218
|
const kept = lines.slice(1).filter((l) => !/@shipeasy[\\/]sdk|see[\\/]core|captureCallsiteStack|\bsee\b\s*\(/.test(l));
|
|
216
219
|
return kept.length ? kept.join("\n") : void 0;
|
|
217
220
|
}
|
|
218
|
-
function buildSeeEvent(problem, consequence, extras, ctx, kindOverride) {
|
|
221
|
+
function buildSeeEvent(problem, consequence, extras, ctx, kindOverride, correlationId) {
|
|
219
222
|
let errorType;
|
|
220
223
|
let message;
|
|
221
224
|
let stack;
|
|
@@ -248,6 +251,7 @@ function buildSeeEvent(problem, consequence, extras, ctx, kindOverride) {
|
|
|
248
251
|
ts: Date.now()
|
|
249
252
|
};
|
|
250
253
|
if (stack) ev.stack = truncate(stack, SEE_MAX_STACK);
|
|
254
|
+
if (correlationId) ev.correlation_id = truncate(String(correlationId), SEE_MAX_CORRELATION);
|
|
251
255
|
const causedBy = findCausedBy(problem);
|
|
252
256
|
if (causedBy) ev.caused_by = causedBy;
|
|
253
257
|
const cleanExtras = sanitizeExtras(extras);
|
|
@@ -501,8 +505,33 @@ function endpointTemplate(rawUrl) {
|
|
|
501
505
|
return (rawUrl.split(/[?#]/)[0] ?? "").slice(0, 120);
|
|
502
506
|
}
|
|
503
507
|
const path = u.pathname.split("/").map((seg) => seg && isIdSegment(seg) ? ":id" : seg).join("/");
|
|
504
|
-
const
|
|
505
|
-
return ((
|
|
508
|
+
const sameOrigin2 = typeof location !== "undefined" && u.origin === location.origin;
|
|
509
|
+
return ((sameOrigin2 ? "" : u.host) + path).slice(0, 120);
|
|
510
|
+
}
|
|
511
|
+
function sameOrigin(rawUrl) {
|
|
512
|
+
if (typeof location === "undefined") return false;
|
|
513
|
+
try {
|
|
514
|
+
return new URL(rawUrl, location.href).origin === location.origin;
|
|
515
|
+
} catch {
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
function injectCorrelationHeader(args, corr) {
|
|
520
|
+
try {
|
|
521
|
+
const input = args[0];
|
|
522
|
+
if (typeof Request !== "undefined" && input instanceof Request) {
|
|
523
|
+
const headers2 = new Headers(input.headers);
|
|
524
|
+
headers2.set("X-SE-Correlation", corr);
|
|
525
|
+
return [new Request(input, { headers: headers2 }), ...args.slice(1)];
|
|
526
|
+
}
|
|
527
|
+
const init = { ...args[1] ?? {} };
|
|
528
|
+
const headers = new Headers(init.headers ?? void 0);
|
|
529
|
+
headers.set("X-SE-Correlation", corr);
|
|
530
|
+
init.headers = headers;
|
|
531
|
+
return [input, init];
|
|
532
|
+
} catch {
|
|
533
|
+
return args;
|
|
534
|
+
}
|
|
506
535
|
}
|
|
507
536
|
function installAutoGuardrails(buffer, userId, anonId, groups, reportSee, ignoreUrlPrefixes, always = false) {
|
|
508
537
|
if (typeof window === "undefined" || typeof PerformanceObserver === "undefined") return;
|
|
@@ -579,6 +608,11 @@ function installAutoGuardrails(buffer, userId, anonId, groups, reportSee, ignore
|
|
|
579
608
|
const url = typeof args[0] === "string" ? args[0] : args[0].toString();
|
|
580
609
|
const ignored = ignoreUrlPrefixes.some((p) => p && url.startsWith(p));
|
|
581
610
|
const bareUrl = url.split("?")[0].slice(0, 200);
|
|
611
|
+
let corr;
|
|
612
|
+
if (!ignored && sameOrigin(url) && typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
613
|
+
corr = crypto.randomUUID();
|
|
614
|
+
args = injectCorrelationHeader(args, corr);
|
|
615
|
+
}
|
|
582
616
|
let res;
|
|
583
617
|
try {
|
|
584
618
|
res = await origFetch.apply(this, args);
|
|
@@ -599,7 +633,8 @@ function installAutoGuardrails(buffer, userId, anonId, groups, reportSee, ignore
|
|
|
599
633
|
violation("Http5xx").message(`request to ${bareUrl} returned ${res.status}`),
|
|
600
634
|
causesThe(`request to ${endpointTemplate(url)}`).to("fail with a server error"),
|
|
601
635
|
{ status: res.status, url: url.slice(0, 200), duration_ms: Math.round(elapsed) },
|
|
602
|
-
"network"
|
|
636
|
+
"network",
|
|
637
|
+
corr
|
|
603
638
|
);
|
|
604
639
|
}
|
|
605
640
|
return res;
|
|
@@ -911,7 +946,7 @@ var FlagsClientBrowser = class {
|
|
|
911
946
|
this.userId,
|
|
912
947
|
this.anonId,
|
|
913
948
|
this.autoGuardrailGroups,
|
|
914
|
-
(problem, consequence, extras, kind) => this.reportError(problem, consequence, extras, kind),
|
|
949
|
+
(problem, consequence, extras, kind, correlationId) => this.reportError(problem, consequence, extras, kind, correlationId),
|
|
915
950
|
[`${this.baseUrl}/`, DEFAULT_TELEMETRY_URL],
|
|
916
951
|
this.autoCollectAlways
|
|
917
952
|
);
|
|
@@ -923,7 +958,7 @@ var FlagsClientBrowser = class {
|
|
|
923
958
|
* (beacon-first) — error occurrences are near-real-time, never queued behind
|
|
924
959
|
* the 5s metric batch. Spam-guarded by a 30s dedup window + per-session cap.
|
|
925
960
|
*/
|
|
926
|
-
reportError(problem, consequence, extras, kind) {
|
|
961
|
+
reportError(problem, consequence, extras, kind, correlationId) {
|
|
927
962
|
try {
|
|
928
963
|
const enriched = { ...collectSeeEnv(), ...extras };
|
|
929
964
|
const ev = buildSeeEvent(problem, consequence, enriched, {
|
|
@@ -933,7 +968,7 @@ var FlagsClientBrowser = class {
|
|
|
933
968
|
url: typeof window !== "undefined" && window.location ? window.location.href : void 0,
|
|
934
969
|
userId: this.userId || void 0,
|
|
935
970
|
anonId: this.anonId
|
|
936
|
-
}, kind);
|
|
971
|
+
}, kind, correlationId);
|
|
937
972
|
if (!this.seeLimiter.shouldSend(ev)) return;
|
|
938
973
|
this.buffer.sendNow([ev]);
|
|
939
974
|
} catch {
|
|
@@ -1600,12 +1635,14 @@ var i18n = {
|
|
|
1600
1635
|
flags,
|
|
1601
1636
|
getShipeasyClient,
|
|
1602
1637
|
i18n,
|
|
1638
|
+
injectCorrelationHeader,
|
|
1603
1639
|
isDevtoolsRequested,
|
|
1604
1640
|
labelAttrs,
|
|
1605
1641
|
loadDevtools,
|
|
1606
1642
|
readConfigOverride,
|
|
1607
1643
|
readExpOverride,
|
|
1608
1644
|
readGateOverride,
|
|
1645
|
+
sameOrigin,
|
|
1609
1646
|
see,
|
|
1610
1647
|
shipeasy,
|
|
1611
1648
|
version
|
package/dist/client/index.mjs
CHANGED
|
@@ -63,6 +63,7 @@ var SEE_MAX_STACK = 8e3;
|
|
|
63
63
|
var SEE_MAX_SUBJECT = 200;
|
|
64
64
|
var SEE_MAX_EXTRA_VALUE = 200;
|
|
65
65
|
var SEE_MAX_EXTRA_KEYS = 20;
|
|
66
|
+
var SEE_MAX_CORRELATION = 64;
|
|
66
67
|
var SEE_DEDUP_WINDOW_MS = 3e4;
|
|
67
68
|
var SEE_MAX_PER_SESSION = 25;
|
|
68
69
|
function causesThe(subject) {
|
|
@@ -169,7 +170,7 @@ function captureCallsiteStack() {
|
|
|
169
170
|
const kept = lines.slice(1).filter((l) => !/@shipeasy[\\/]sdk|see[\\/]core|captureCallsiteStack|\bsee\b\s*\(/.test(l));
|
|
170
171
|
return kept.length ? kept.join("\n") : void 0;
|
|
171
172
|
}
|
|
172
|
-
function buildSeeEvent(problem, consequence, extras, ctx, kindOverride) {
|
|
173
|
+
function buildSeeEvent(problem, consequence, extras, ctx, kindOverride, correlationId) {
|
|
173
174
|
let errorType;
|
|
174
175
|
let message;
|
|
175
176
|
let stack;
|
|
@@ -202,6 +203,7 @@ function buildSeeEvent(problem, consequence, extras, ctx, kindOverride) {
|
|
|
202
203
|
ts: Date.now()
|
|
203
204
|
};
|
|
204
205
|
if (stack) ev.stack = truncate(stack, SEE_MAX_STACK);
|
|
206
|
+
if (correlationId) ev.correlation_id = truncate(String(correlationId), SEE_MAX_CORRELATION);
|
|
205
207
|
const causedBy = findCausedBy(problem);
|
|
206
208
|
if (causedBy) ev.caused_by = causedBy;
|
|
207
209
|
const cleanExtras = sanitizeExtras(extras);
|
|
@@ -455,8 +457,33 @@ function endpointTemplate(rawUrl) {
|
|
|
455
457
|
return (rawUrl.split(/[?#]/)[0] ?? "").slice(0, 120);
|
|
456
458
|
}
|
|
457
459
|
const path = u.pathname.split("/").map((seg) => seg && isIdSegment(seg) ? ":id" : seg).join("/");
|
|
458
|
-
const
|
|
459
|
-
return ((
|
|
460
|
+
const sameOrigin2 = typeof location !== "undefined" && u.origin === location.origin;
|
|
461
|
+
return ((sameOrigin2 ? "" : u.host) + path).slice(0, 120);
|
|
462
|
+
}
|
|
463
|
+
function sameOrigin(rawUrl) {
|
|
464
|
+
if (typeof location === "undefined") return false;
|
|
465
|
+
try {
|
|
466
|
+
return new URL(rawUrl, location.href).origin === location.origin;
|
|
467
|
+
} catch {
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
function injectCorrelationHeader(args, corr) {
|
|
472
|
+
try {
|
|
473
|
+
const input = args[0];
|
|
474
|
+
if (typeof Request !== "undefined" && input instanceof Request) {
|
|
475
|
+
const headers2 = new Headers(input.headers);
|
|
476
|
+
headers2.set("X-SE-Correlation", corr);
|
|
477
|
+
return [new Request(input, { headers: headers2 }), ...args.slice(1)];
|
|
478
|
+
}
|
|
479
|
+
const init = { ...args[1] ?? {} };
|
|
480
|
+
const headers = new Headers(init.headers ?? void 0);
|
|
481
|
+
headers.set("X-SE-Correlation", corr);
|
|
482
|
+
init.headers = headers;
|
|
483
|
+
return [input, init];
|
|
484
|
+
} catch {
|
|
485
|
+
return args;
|
|
486
|
+
}
|
|
460
487
|
}
|
|
461
488
|
function installAutoGuardrails(buffer, userId, anonId, groups, reportSee, ignoreUrlPrefixes, always = false) {
|
|
462
489
|
if (typeof window === "undefined" || typeof PerformanceObserver === "undefined") return;
|
|
@@ -533,6 +560,11 @@ function installAutoGuardrails(buffer, userId, anonId, groups, reportSee, ignore
|
|
|
533
560
|
const url = typeof args[0] === "string" ? args[0] : args[0].toString();
|
|
534
561
|
const ignored = ignoreUrlPrefixes.some((p) => p && url.startsWith(p));
|
|
535
562
|
const bareUrl = url.split("?")[0].slice(0, 200);
|
|
563
|
+
let corr;
|
|
564
|
+
if (!ignored && sameOrigin(url) && typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
565
|
+
corr = crypto.randomUUID();
|
|
566
|
+
args = injectCorrelationHeader(args, corr);
|
|
567
|
+
}
|
|
536
568
|
let res;
|
|
537
569
|
try {
|
|
538
570
|
res = await origFetch.apply(this, args);
|
|
@@ -553,7 +585,8 @@ function installAutoGuardrails(buffer, userId, anonId, groups, reportSee, ignore
|
|
|
553
585
|
violation("Http5xx").message(`request to ${bareUrl} returned ${res.status}`),
|
|
554
586
|
causesThe(`request to ${endpointTemplate(url)}`).to("fail with a server error"),
|
|
555
587
|
{ status: res.status, url: url.slice(0, 200), duration_ms: Math.round(elapsed) },
|
|
556
|
-
"network"
|
|
588
|
+
"network",
|
|
589
|
+
corr
|
|
557
590
|
);
|
|
558
591
|
}
|
|
559
592
|
return res;
|
|
@@ -865,7 +898,7 @@ var FlagsClientBrowser = class {
|
|
|
865
898
|
this.userId,
|
|
866
899
|
this.anonId,
|
|
867
900
|
this.autoGuardrailGroups,
|
|
868
|
-
(problem, consequence, extras, kind) => this.reportError(problem, consequence, extras, kind),
|
|
901
|
+
(problem, consequence, extras, kind, correlationId) => this.reportError(problem, consequence, extras, kind, correlationId),
|
|
869
902
|
[`${this.baseUrl}/`, DEFAULT_TELEMETRY_URL],
|
|
870
903
|
this.autoCollectAlways
|
|
871
904
|
);
|
|
@@ -877,7 +910,7 @@ var FlagsClientBrowser = class {
|
|
|
877
910
|
* (beacon-first) — error occurrences are near-real-time, never queued behind
|
|
878
911
|
* the 5s metric batch. Spam-guarded by a 30s dedup window + per-session cap.
|
|
879
912
|
*/
|
|
880
|
-
reportError(problem, consequence, extras, kind) {
|
|
913
|
+
reportError(problem, consequence, extras, kind, correlationId) {
|
|
881
914
|
try {
|
|
882
915
|
const enriched = { ...collectSeeEnv(), ...extras };
|
|
883
916
|
const ev = buildSeeEvent(problem, consequence, enriched, {
|
|
@@ -887,7 +920,7 @@ var FlagsClientBrowser = class {
|
|
|
887
920
|
url: typeof window !== "undefined" && window.location ? window.location.href : void 0,
|
|
888
921
|
userId: this.userId || void 0,
|
|
889
922
|
anonId: this.anonId
|
|
890
|
-
}, kind);
|
|
923
|
+
}, kind, correlationId);
|
|
891
924
|
if (!this.seeLimiter.shouldSend(ev)) return;
|
|
892
925
|
this.buffer.sendNow([ev]);
|
|
893
926
|
} catch {
|
|
@@ -1553,12 +1586,14 @@ export {
|
|
|
1553
1586
|
flags,
|
|
1554
1587
|
getShipeasyClient,
|
|
1555
1588
|
i18n,
|
|
1589
|
+
injectCorrelationHeader,
|
|
1556
1590
|
isDevtoolsRequested,
|
|
1557
1591
|
labelAttrs,
|
|
1558
1592
|
loadDevtools,
|
|
1559
1593
|
readConfigOverride,
|
|
1560
1594
|
readExpOverride,
|
|
1561
1595
|
readGateOverride,
|
|
1596
|
+
sameOrigin,
|
|
1562
1597
|
see,
|
|
1563
1598
|
shipeasy,
|
|
1564
1599
|
version
|
package/dist/server/index.d.mts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
|
|
1
3
|
type SeeExtras = Record<string, string | number | boolean | null | undefined>;
|
|
2
4
|
type SeeKind = "caught" | "uncaught" | "unhandled_rejection" | "network" | "violation";
|
|
3
5
|
/** Built by `causesThe(subject).to(outcome)` — never constructed by hand. */
|
|
@@ -51,6 +53,16 @@ interface SeeErrorEvent {
|
|
|
51
53
|
env?: string;
|
|
52
54
|
sdk_version: string;
|
|
53
55
|
ts: number;
|
|
56
|
+
/**
|
|
57
|
+
* Per-request correlation token. The client mints one per same-origin fetch
|
|
58
|
+
* and ships it on both the request header (`X-SE-Correlation`) and any 5xx
|
|
59
|
+
* occurrence it reports; the server safety net reports the matching uncaught
|
|
60
|
+
* error under the same token. The backend joins the two issues by it —
|
|
61
|
+
* populating `caused_by` across the network boundary, where the in-process
|
|
62
|
+
* `.cause`-chain stamp (see `findCausedBy`) cannot reach. Join-only metadata,
|
|
63
|
+
* never persisted as an issue field.
|
|
64
|
+
*/
|
|
65
|
+
correlation_id?: string;
|
|
54
66
|
/**
|
|
55
67
|
* The earlier reported problem this occurrence descends from — present when
|
|
56
68
|
* the same error was caught + reported at an inner boundary and then
|
|
@@ -60,6 +72,7 @@ interface SeeErrorEvent {
|
|
|
60
72
|
*/
|
|
61
73
|
caused_by?: SeeCausedBy;
|
|
62
74
|
}
|
|
75
|
+
declare function isExpected(err: unknown): boolean;
|
|
63
76
|
interface SeeExtrasTail {
|
|
64
77
|
/** Attach debugging metadata. Callable repeatedly — keys merge, later wins. */
|
|
65
78
|
extras(extras: SeeExtras): SeeExtrasTail;
|
|
@@ -187,6 +200,11 @@ declare class FlagsClient {
|
|
|
187
200
|
evaluate(user: User, rawUrl?: string): BootstrapPayload;
|
|
188
201
|
getKillswitch(name: string, switchKey?: string): boolean;
|
|
189
202
|
}
|
|
203
|
+
interface SeeCorrelationStore {
|
|
204
|
+
correlationId?: string;
|
|
205
|
+
}
|
|
206
|
+
declare const seeContext: AsyncLocalStorage<SeeCorrelationStore>;
|
|
207
|
+
|
|
190
208
|
interface I18nForRequest {
|
|
191
209
|
strings: Record<string, string>;
|
|
192
210
|
locale: string;
|
|
@@ -362,4 +380,4 @@ interface SeeApi {
|
|
|
362
380
|
*/
|
|
363
381
|
declare const see: SeeApi;
|
|
364
382
|
|
|
365
|
-
export { type BootstrapHtmlOptions, type BootstrapPayload, type Consequence, type ExperimentResult, type FetchLabelsOptions, FlagsClient, type FlagsClientEnv, type FlagsClientOptions, type I18nForRequest, type LabelFile, type SeeApi, type SeeChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyServerConfig, type ShipeasyServerHandle, type User, type Violation, _resetShipeasyServerForTests, configureShipeasyServer, fetchLabelsForSSR, flags, getBootstrapHtml, getShipeasyServerClient, i18n, see, shipeasy, version };
|
|
383
|
+
export { type BootstrapHtmlOptions, type BootstrapPayload, type Consequence, type ExperimentResult, type FetchLabelsOptions, FlagsClient, type FlagsClientEnv, type FlagsClientOptions, type I18nForRequest, type LabelFile, type SeeApi, type SeeChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyServerConfig, type ShipeasyServerHandle, type User, type Violation, _resetShipeasyServerForTests, configureShipeasyServer, fetchLabelsForSSR, flags, getBootstrapHtml, getShipeasyServerClient, i18n, isExpected, see, seeContext, shipeasy, version };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
|
|
1
3
|
type SeeExtras = Record<string, string | number | boolean | null | undefined>;
|
|
2
4
|
type SeeKind = "caught" | "uncaught" | "unhandled_rejection" | "network" | "violation";
|
|
3
5
|
/** Built by `causesThe(subject).to(outcome)` — never constructed by hand. */
|
|
@@ -51,6 +53,16 @@ interface SeeErrorEvent {
|
|
|
51
53
|
env?: string;
|
|
52
54
|
sdk_version: string;
|
|
53
55
|
ts: number;
|
|
56
|
+
/**
|
|
57
|
+
* Per-request correlation token. The client mints one per same-origin fetch
|
|
58
|
+
* and ships it on both the request header (`X-SE-Correlation`) and any 5xx
|
|
59
|
+
* occurrence it reports; the server safety net reports the matching uncaught
|
|
60
|
+
* error under the same token. The backend joins the two issues by it —
|
|
61
|
+
* populating `caused_by` across the network boundary, where the in-process
|
|
62
|
+
* `.cause`-chain stamp (see `findCausedBy`) cannot reach. Join-only metadata,
|
|
63
|
+
* never persisted as an issue field.
|
|
64
|
+
*/
|
|
65
|
+
correlation_id?: string;
|
|
54
66
|
/**
|
|
55
67
|
* The earlier reported problem this occurrence descends from — present when
|
|
56
68
|
* the same error was caught + reported at an inner boundary and then
|
|
@@ -60,6 +72,7 @@ interface SeeErrorEvent {
|
|
|
60
72
|
*/
|
|
61
73
|
caused_by?: SeeCausedBy;
|
|
62
74
|
}
|
|
75
|
+
declare function isExpected(err: unknown): boolean;
|
|
63
76
|
interface SeeExtrasTail {
|
|
64
77
|
/** Attach debugging metadata. Callable repeatedly — keys merge, later wins. */
|
|
65
78
|
extras(extras: SeeExtras): SeeExtrasTail;
|
|
@@ -187,6 +200,11 @@ declare class FlagsClient {
|
|
|
187
200
|
evaluate(user: User, rawUrl?: string): BootstrapPayload;
|
|
188
201
|
getKillswitch(name: string, switchKey?: string): boolean;
|
|
189
202
|
}
|
|
203
|
+
interface SeeCorrelationStore {
|
|
204
|
+
correlationId?: string;
|
|
205
|
+
}
|
|
206
|
+
declare const seeContext: AsyncLocalStorage<SeeCorrelationStore>;
|
|
207
|
+
|
|
190
208
|
interface I18nForRequest {
|
|
191
209
|
strings: Record<string, string>;
|
|
192
210
|
locale: string;
|
|
@@ -362,4 +380,4 @@ interface SeeApi {
|
|
|
362
380
|
*/
|
|
363
381
|
declare const see: SeeApi;
|
|
364
382
|
|
|
365
|
-
export { type BootstrapHtmlOptions, type BootstrapPayload, type Consequence, type ExperimentResult, type FetchLabelsOptions, FlagsClient, type FlagsClientEnv, type FlagsClientOptions, type I18nForRequest, type LabelFile, type SeeApi, type SeeChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyServerConfig, type ShipeasyServerHandle, type User, type Violation, _resetShipeasyServerForTests, configureShipeasyServer, fetchLabelsForSSR, flags, getBootstrapHtml, getShipeasyServerClient, i18n, see, shipeasy, version };
|
|
383
|
+
export { type BootstrapHtmlOptions, type BootstrapPayload, type Consequence, type ExperimentResult, type FetchLabelsOptions, FlagsClient, type FlagsClientEnv, type FlagsClientOptions, type I18nForRequest, type LabelFile, type SeeApi, type SeeChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyServerConfig, type ShipeasyServerHandle, type User, type Violation, _resetShipeasyServerForTests, configureShipeasyServer, fetchLabelsForSSR, flags, getBootstrapHtml, getShipeasyServerClient, i18n, isExpected, see, seeContext, shipeasy, version };
|
package/dist/server/index.js
CHANGED
|
@@ -38,7 +38,9 @@ __export(server_exports, {
|
|
|
38
38
|
getBootstrapHtml: () => getBootstrapHtml,
|
|
39
39
|
getShipeasyServerClient: () => getShipeasyServerClient,
|
|
40
40
|
i18n: () => i18n,
|
|
41
|
+
isExpected: () => isExpected,
|
|
41
42
|
see: () => see,
|
|
43
|
+
seeContext: () => seeContext,
|
|
42
44
|
shipeasy: () => shipeasy,
|
|
43
45
|
version: () => version
|
|
44
46
|
});
|
|
@@ -110,6 +112,7 @@ var SEE_MAX_STACK = 8e3;
|
|
|
110
112
|
var SEE_MAX_SUBJECT = 200;
|
|
111
113
|
var SEE_MAX_EXTRA_VALUE = 200;
|
|
112
114
|
var SEE_MAX_EXTRA_KEYS = 20;
|
|
115
|
+
var SEE_MAX_CORRELATION = 64;
|
|
113
116
|
var SEE_DEDUP_WINDOW_MS = 3e4;
|
|
114
117
|
var SEE_MAX_PER_SESSION = 25;
|
|
115
118
|
function causesThe(subject) {
|
|
@@ -149,6 +152,10 @@ function markExpected(err, because) {
|
|
|
149
152
|
} catch {
|
|
150
153
|
}
|
|
151
154
|
}
|
|
155
|
+
function isExpected(err) {
|
|
156
|
+
if (typeof err !== "object" || err === null) return false;
|
|
157
|
+
return err[EXPECTED_SYM] !== void 0;
|
|
158
|
+
}
|
|
152
159
|
var REPORTED_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:see-reported");
|
|
153
160
|
var SEE_MAX_CAUSE_DEPTH = 8;
|
|
154
161
|
function readReportStamp(err) {
|
|
@@ -212,7 +219,7 @@ function captureCallsiteStack() {
|
|
|
212
219
|
const kept = lines.slice(1).filter((l) => !/@shipeasy[\\/]sdk|see[\\/]core|captureCallsiteStack|\bsee\b\s*\(/.test(l));
|
|
213
220
|
return kept.length ? kept.join("\n") : void 0;
|
|
214
221
|
}
|
|
215
|
-
function buildSeeEvent(problem, consequence, extras, ctx, kindOverride) {
|
|
222
|
+
function buildSeeEvent(problem, consequence, extras, ctx, kindOverride, correlationId) {
|
|
216
223
|
let errorType;
|
|
217
224
|
let message;
|
|
218
225
|
let stack;
|
|
@@ -245,6 +252,7 @@ function buildSeeEvent(problem, consequence, extras, ctx, kindOverride) {
|
|
|
245
252
|
ts: Date.now()
|
|
246
253
|
};
|
|
247
254
|
if (stack) ev.stack = truncate(stack, SEE_MAX_STACK);
|
|
255
|
+
if (correlationId) ev.correlation_id = truncate(String(correlationId), SEE_MAX_CORRELATION);
|
|
248
256
|
const causedBy = findCausedBy(problem);
|
|
249
257
|
if (causedBy) ev.caused_by = causedBy;
|
|
250
258
|
const cleanExtras = sanitizeExtras(extras);
|
|
@@ -654,11 +662,12 @@ var FlagsClient = class {
|
|
|
654
662
|
*/
|
|
655
663
|
reportError(problem, consequence, extras, kind) {
|
|
656
664
|
try {
|
|
665
|
+
const correlationId = seeContext.getStore()?.correlationId;
|
|
657
666
|
const ev = buildSeeEvent(problem, consequence, extras, {
|
|
658
667
|
side: "server",
|
|
659
668
|
sdkVersion: version,
|
|
660
669
|
env: this.env
|
|
661
|
-
}, kind);
|
|
670
|
+
}, kind, correlationId);
|
|
662
671
|
if (!this.seeLimiter.shouldSend(ev)) return;
|
|
663
672
|
globalThis.fetch(`${this.baseUrl}/collect`, {
|
|
664
673
|
method: "POST",
|
|
@@ -753,6 +762,8 @@ Object.defineProperty(globalThis, _EDIT_MODE_SSR_SYM, {
|
|
|
753
762
|
},
|
|
754
763
|
configurable: true
|
|
755
764
|
});
|
|
765
|
+
var _SEE_CORR_ALS_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:see-correlation-als");
|
|
766
|
+
var seeContext = globalThis[_SEE_CORR_ALS_SYM] ?? (globalThis[_SEE_CORR_ALS_SYM] = new import_node_async_hooks.AsyncLocalStorage());
|
|
756
767
|
var i18n = {
|
|
757
768
|
/**
|
|
758
769
|
* Fetch translation labels for the current request and store them in an
|
|
@@ -1000,7 +1011,9 @@ var see = Object.assign(
|
|
|
1000
1011
|
getBootstrapHtml,
|
|
1001
1012
|
getShipeasyServerClient,
|
|
1002
1013
|
i18n,
|
|
1014
|
+
isExpected,
|
|
1003
1015
|
see,
|
|
1016
|
+
seeContext,
|
|
1004
1017
|
shipeasy,
|
|
1005
1018
|
version
|
|
1006
1019
|
});
|
package/dist/server/index.mjs
CHANGED
|
@@ -66,6 +66,7 @@ var SEE_MAX_STACK = 8e3;
|
|
|
66
66
|
var SEE_MAX_SUBJECT = 200;
|
|
67
67
|
var SEE_MAX_EXTRA_VALUE = 200;
|
|
68
68
|
var SEE_MAX_EXTRA_KEYS = 20;
|
|
69
|
+
var SEE_MAX_CORRELATION = 64;
|
|
69
70
|
var SEE_DEDUP_WINDOW_MS = 3e4;
|
|
70
71
|
var SEE_MAX_PER_SESSION = 25;
|
|
71
72
|
function causesThe(subject) {
|
|
@@ -105,6 +106,10 @@ function markExpected(err, because) {
|
|
|
105
106
|
} catch {
|
|
106
107
|
}
|
|
107
108
|
}
|
|
109
|
+
function isExpected(err) {
|
|
110
|
+
if (typeof err !== "object" || err === null) return false;
|
|
111
|
+
return err[EXPECTED_SYM] !== void 0;
|
|
112
|
+
}
|
|
108
113
|
var REPORTED_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:see-reported");
|
|
109
114
|
var SEE_MAX_CAUSE_DEPTH = 8;
|
|
110
115
|
function readReportStamp(err) {
|
|
@@ -168,7 +173,7 @@ function captureCallsiteStack() {
|
|
|
168
173
|
const kept = lines.slice(1).filter((l) => !/@shipeasy[\\/]sdk|see[\\/]core|captureCallsiteStack|\bsee\b\s*\(/.test(l));
|
|
169
174
|
return kept.length ? kept.join("\n") : void 0;
|
|
170
175
|
}
|
|
171
|
-
function buildSeeEvent(problem, consequence, extras, ctx, kindOverride) {
|
|
176
|
+
function buildSeeEvent(problem, consequence, extras, ctx, kindOverride, correlationId) {
|
|
172
177
|
let errorType;
|
|
173
178
|
let message;
|
|
174
179
|
let stack;
|
|
@@ -201,6 +206,7 @@ function buildSeeEvent(problem, consequence, extras, ctx, kindOverride) {
|
|
|
201
206
|
ts: Date.now()
|
|
202
207
|
};
|
|
203
208
|
if (stack) ev.stack = truncate(stack, SEE_MAX_STACK);
|
|
209
|
+
if (correlationId) ev.correlation_id = truncate(String(correlationId), SEE_MAX_CORRELATION);
|
|
204
210
|
const causedBy = findCausedBy(problem);
|
|
205
211
|
if (causedBy) ev.caused_by = causedBy;
|
|
206
212
|
const cleanExtras = sanitizeExtras(extras);
|
|
@@ -610,11 +616,12 @@ var FlagsClient = class {
|
|
|
610
616
|
*/
|
|
611
617
|
reportError(problem, consequence, extras, kind) {
|
|
612
618
|
try {
|
|
619
|
+
const correlationId = seeContext.getStore()?.correlationId;
|
|
613
620
|
const ev = buildSeeEvent(problem, consequence, extras, {
|
|
614
621
|
side: "server",
|
|
615
622
|
sdkVersion: version,
|
|
616
623
|
env: this.env
|
|
617
|
-
}, kind);
|
|
624
|
+
}, kind, correlationId);
|
|
618
625
|
if (!this.seeLimiter.shouldSend(ev)) return;
|
|
619
626
|
globalThis.fetch(`${this.baseUrl}/collect`, {
|
|
620
627
|
method: "POST",
|
|
@@ -709,6 +716,8 @@ Object.defineProperty(globalThis, _EDIT_MODE_SSR_SYM, {
|
|
|
709
716
|
},
|
|
710
717
|
configurable: true
|
|
711
718
|
});
|
|
719
|
+
var _SEE_CORR_ALS_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:see-correlation-als");
|
|
720
|
+
var seeContext = globalThis[_SEE_CORR_ALS_SYM] ?? (globalThis[_SEE_CORR_ALS_SYM] = new AsyncLocalStorage());
|
|
712
721
|
var i18n = {
|
|
713
722
|
/**
|
|
714
723
|
* Fetch translation labels for the current request and store them in an
|
|
@@ -955,7 +964,9 @@ export {
|
|
|
955
964
|
getBootstrapHtml,
|
|
956
965
|
getShipeasyServerClient,
|
|
957
966
|
i18n,
|
|
967
|
+
isExpected,
|
|
958
968
|
see,
|
|
969
|
+
seeContext,
|
|
959
970
|
shipeasy,
|
|
960
971
|
version
|
|
961
972
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipeasy/sdk",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.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",
|