@shipeasy/sdk 3.0.1 → 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 +165 -7
- package/dist/client/index.d.ts +165 -7
- package/dist/client/index.js +396 -51
- package/dist/client/index.mjs +393 -51
- package/dist/server/index.d.mts +130 -2
- package/dist/server/index.d.ts +130 -2
- package/dist/server/index.js +312 -2
- package/dist/server/index.mjs +311 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,6 +53,58 @@ const client = new FlagsClient({ sdkKey: process.env.SHIPEASY_SERVER_KEY! });
|
|
|
53
53
|
const cfg = await client.getConfig("plan_limits", { user_id: "u-1" });
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
## Error tracking — `see()`
|
|
57
|
+
|
|
58
|
+
`see` (shipeasy error) is the structured error reporter — opes-style: every
|
|
59
|
+
handled exception documents its **product consequence**, not just its stack.
|
|
60
|
+
Works in vanilla JS on both sides; the whole grammar hangs off one import:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { see } from "@shipeasy/sdk/client"; // or "@shipeasy/sdk/server"
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
await submitOrder(order);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
see(e).causes_the("checkout").to("use cached prices").extras({ order_id: order.id });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Non-exception problems — the name is a stable identifier (it participates
|
|
72
|
+
// in the issue fingerprint); variable data goes in .message() / .extras():
|
|
73
|
+
if (rows.length > LIMIT) {
|
|
74
|
+
see.Violation("large query")
|
|
75
|
+
.message(`got ${rows.length} rows`)
|
|
76
|
+
.causes_the("search results")
|
|
77
|
+
.to("be trimmed");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Expected control-flow exceptions: document them, report nothing —
|
|
81
|
+
// auto-capture skips marked errors. The reason must start with "because".
|
|
82
|
+
try {
|
|
83
|
+
return decodeFoo(blob);
|
|
84
|
+
} catch (e) {
|
|
85
|
+
see.ControlFlowException(e, "because it wasn't an encoded Foo");
|
|
86
|
+
return decodeBar(blob);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Reports land in the Shipeasy **errors** primitive: fingerprint-grouped issues
|
|
91
|
+
(open / resolved / ignored, regression auto-reopens) with a near-real-time
|
|
92
|
+
occurrence timeseries. The chain dispatches on the next microtask — no
|
|
93
|
+
`.send()` — and ships immediately (`sendBeacon` in the browser, fire-and-forget
|
|
94
|
+
`fetch` on the server), spam-guarded by a 30s dedup window and a per-session
|
|
95
|
+
cap.
|
|
96
|
+
|
|
97
|
+
The client SDK also auto-captures uncaught exceptions, unhandled rejections,
|
|
98
|
+
and network failures (fetch network errors + 5xx) into the same primitive
|
|
99
|
+
(`autoCollect: { errors }`, on by default). Auto-capture is the outer safety
|
|
100
|
+
net — it does not replace `see()` in catch blocks, where you know the
|
|
101
|
+
consequence and it cannot.
|
|
102
|
+
|
|
103
|
+
**Rules**: if you don't know the consequence, don't catch the exception. Never
|
|
104
|
+
`see()` then `throw` (double counting — either handle or rethrow). Never use
|
|
105
|
+
`see.Violation()` for a caught exception (you'd drop the stack). No PII or
|
|
106
|
+
high-cardinality data in extras.
|
|
107
|
+
|
|
56
108
|
## Drop-in `<script>` loader (no bundler)
|
|
57
109
|
|
|
58
110
|
```html
|
package/dist/client/index.d.mts
CHANGED
|
@@ -1,3 +1,62 @@
|
|
|
1
|
+
type SeeExtras = Record<string, string | number | boolean | null | undefined>;
|
|
2
|
+
type SeeKind = "caught" | "uncaught" | "unhandled_rejection" | "network" | "violation";
|
|
3
|
+
/** Built by `causesThe(subject).to(outcome)` — never constructed by hand. */
|
|
4
|
+
interface Consequence {
|
|
5
|
+
readonly __seConsequence: true;
|
|
6
|
+
readonly subject: string;
|
|
7
|
+
readonly outcome: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Non-exception problem, built by `violation(name)`. A plain branded object
|
|
11
|
+
* (not an Error subclass) so `.message()` can be a builder method without
|
|
12
|
+
* colliding with `Error.prototype.message`.
|
|
13
|
+
*/
|
|
14
|
+
interface Violation {
|
|
15
|
+
readonly __seViolation: true;
|
|
16
|
+
readonly violationName: string;
|
|
17
|
+
readonly violationMessage?: string;
|
|
18
|
+
/** Attach free-form detail. Variable data goes HERE (or in extras), never in the name. */
|
|
19
|
+
message(msg: string): Violation;
|
|
20
|
+
}
|
|
21
|
+
/** Wire shape — the `type:"error"` RawEvent variant accepted by POST /collect. */
|
|
22
|
+
interface SeeErrorEvent {
|
|
23
|
+
type: "error";
|
|
24
|
+
kind: SeeKind;
|
|
25
|
+
/** Error class/name (e.g. "TypeError") or the violation name. */
|
|
26
|
+
error_type: string;
|
|
27
|
+
message: string;
|
|
28
|
+
stack?: string;
|
|
29
|
+
/** Consequence: "<error_type> causes the <subject> to <outcome>". */
|
|
30
|
+
subject: string;
|
|
31
|
+
outcome: string;
|
|
32
|
+
extras?: Record<string, string | number | boolean>;
|
|
33
|
+
url?: string;
|
|
34
|
+
user_id?: string;
|
|
35
|
+
anonymous_id?: string;
|
|
36
|
+
side: "client" | "server";
|
|
37
|
+
env?: string;
|
|
38
|
+
sdk_version: string;
|
|
39
|
+
ts: number;
|
|
40
|
+
}
|
|
41
|
+
interface SeeExtrasTail {
|
|
42
|
+
/** Attach debugging metadata. Callable repeatedly — keys merge, later wins. */
|
|
43
|
+
extras(extras: SeeExtras): SeeExtrasTail;
|
|
44
|
+
}
|
|
45
|
+
interface SeeOutcomeStep {
|
|
46
|
+
/** The user-visible impact: `.causes_the("checkout").to("use cached prices")`. */
|
|
47
|
+
to(outcome: string): SeeExtrasTail;
|
|
48
|
+
}
|
|
49
|
+
interface SeeChain {
|
|
50
|
+
/** Start the consequence sentence — the product surface affected. */
|
|
51
|
+
causes_the(subject: string): SeeOutcomeStep;
|
|
52
|
+
/** camelCase alias of {@link SeeChain.causes_the}. */
|
|
53
|
+
causesThe(subject: string): SeeOutcomeStep;
|
|
54
|
+
}
|
|
55
|
+
interface SeeViolationChain extends SeeChain {
|
|
56
|
+
/** Free-form detail. Variable data goes here (or extras), never in the name. */
|
|
57
|
+
message(msg: string): SeeViolationChain;
|
|
58
|
+
}
|
|
59
|
+
|
|
1
60
|
declare global {
|
|
2
61
|
interface Window {
|
|
3
62
|
i18n?: {
|
|
@@ -8,7 +67,7 @@ declare global {
|
|
|
8
67
|
};
|
|
9
68
|
}
|
|
10
69
|
}
|
|
11
|
-
declare const version = "
|
|
70
|
+
declare const version = "4.0.0";
|
|
12
71
|
interface User {
|
|
13
72
|
user_id?: string;
|
|
14
73
|
[attr: string]: unknown;
|
|
@@ -51,19 +110,38 @@ interface FlagsClientBrowserOptions {
|
|
|
51
110
|
* true when `autoGuardrails` is true).
|
|
52
111
|
*/
|
|
53
112
|
autoGuardrailGroups?: Partial<AutoCollectGroups>;
|
|
113
|
+
/**
|
|
114
|
+
* Emit `__auto_*` metric events for ALL visitors, not just experiment
|
|
115
|
+
* participants. Default false: auto-metrics are gated on the visitor having
|
|
116
|
+
* seen ≥1 experiment exposure (the only data the analysis pipeline reads;
|
|
117
|
+
* ungated emission is pure AE write cost at scale — see cost.md).
|
|
118
|
+
*/
|
|
119
|
+
autoCollectAlways?: boolean;
|
|
54
120
|
/** Which published env to read values from. Defaults to "prod". */
|
|
55
121
|
env?: FlagsClientBrowserEnv;
|
|
122
|
+
/**
|
|
123
|
+
* Per-evaluation usage telemetry. ON by default — each getFlag/getConfig/
|
|
124
|
+
* getExperiment/getKillswitch call fires one fire-and-forget sendBeacon so
|
|
125
|
+
* usage is counted by Cloudflare's native per-path analytics. Pass `true` to
|
|
126
|
+
* disable entirely.
|
|
127
|
+
*/
|
|
128
|
+
disableTelemetry?: boolean;
|
|
129
|
+
/** Override the telemetry beacon host. Defaults to {@link DEFAULT_TELEMETRY_URL}. */
|
|
130
|
+
telemetryUrl?: string;
|
|
56
131
|
}
|
|
57
132
|
declare class FlagsClientBrowser {
|
|
58
133
|
private readonly sdkKey;
|
|
59
134
|
private readonly baseUrl;
|
|
60
135
|
private readonly autoGuardrails;
|
|
61
136
|
private readonly autoGuardrailGroups;
|
|
137
|
+
private readonly autoCollectAlways;
|
|
62
138
|
private readonly env;
|
|
63
139
|
private evalResult;
|
|
64
140
|
private anonId;
|
|
65
141
|
private userId;
|
|
66
142
|
private buffer;
|
|
143
|
+
private telemetry;
|
|
144
|
+
private seeLimiter;
|
|
67
145
|
private guardrailsInstalled;
|
|
68
146
|
private listeners;
|
|
69
147
|
private overrideListenerInstalled;
|
|
@@ -71,6 +149,12 @@ declare class FlagsClientBrowser {
|
|
|
71
149
|
private onOverrideChange;
|
|
72
150
|
constructor(opts: FlagsClientBrowserOptions);
|
|
73
151
|
identify(user: User): Promise<void>;
|
|
152
|
+
/**
|
|
153
|
+
* Report a structured error into the errors primitive. Flushes immediately
|
|
154
|
+
* (beacon-first) — error occurrences are near-real-time, never queued behind
|
|
155
|
+
* the 5s metric batch. Spam-guarded by a 30s dedup window + per-session cap.
|
|
156
|
+
*/
|
|
157
|
+
reportError(problem: unknown, consequence: Consequence, extras?: SeeExtras, kind?: SeeKind): void;
|
|
74
158
|
get ready(): boolean;
|
|
75
159
|
private notify;
|
|
76
160
|
initFromBootstrap(data: EvalResponse): void;
|
|
@@ -161,10 +245,11 @@ interface ShipeasyClientConfig {
|
|
|
161
245
|
*/
|
|
162
246
|
autoIdentify?: boolean;
|
|
163
247
|
/**
|
|
164
|
-
* Capture web vitals (LCP, CLS, INP, TTFB, FCP, navigation timing)
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
248
|
+
* Capture web vitals (LCP, CLS, INP, TTFB, FCP, navigation timing) and
|
|
249
|
+
* engagement signals (abandonment) as `__auto_*` metric events, plus JS /
|
|
250
|
+
* network errors as structured error events in the errors primitive (same
|
|
251
|
+
* pipeline as `see()` — grouped by fingerprint, near-real-time). Defaults
|
|
252
|
+
* to `true`.
|
|
168
253
|
*
|
|
169
254
|
* Pass `false` to disable everything, or a per-group object to narrow:
|
|
170
255
|
*
|
|
@@ -173,8 +258,29 @@ interface ShipeasyClientConfig {
|
|
|
173
258
|
* shipeasy({ clientKey, autoCollect: { errors: false } }); // vitals + engagement only
|
|
174
259
|
* shipeasy({ clientKey }); // all groups on
|
|
175
260
|
* ```
|
|
261
|
+
*
|
|
262
|
+
* Since 4.1.0, `__auto_*` metric events are only emitted for visitors who
|
|
263
|
+
* are in ≥1 active experiment (the analysis pipeline reads nothing else;
|
|
264
|
+
* gating cuts the dominant Analytics Engine write cost at scale). Pass
|
|
265
|
+
* `{ always: true }` to collect site-wide vitals regardless of experiment
|
|
266
|
+
* participation:
|
|
267
|
+
*
|
|
268
|
+
* ```ts
|
|
269
|
+
* shipeasy({ clientKey, autoCollect: { always: true } }); // site-wide vitals
|
|
270
|
+
* ```
|
|
271
|
+
*
|
|
272
|
+
* `__auto_abandoned` is always emitted (it fires when the user leaves
|
|
273
|
+
* before exposures could land) and error capture is unaffected.
|
|
176
274
|
*/
|
|
177
|
-
autoCollect?: boolean | Partial<AutoCollectGroups
|
|
275
|
+
autoCollect?: boolean | (Partial<AutoCollectGroups> & {
|
|
276
|
+
always?: boolean;
|
|
277
|
+
});
|
|
278
|
+
/**
|
|
279
|
+
* Disable per-evaluation usage telemetry. Telemetry is ON by default — every
|
|
280
|
+
* flag/config/experiment/killswitch read fires one fire-and-forget beacon
|
|
281
|
+
* counted by Cloudflare's native per-path analytics. Pass `true` to opt out.
|
|
282
|
+
*/
|
|
283
|
+
disableTelemetry?: boolean;
|
|
178
284
|
}
|
|
179
285
|
/**
|
|
180
286
|
* Initialise the ShipEasy client SDK and wire up lazy devtools.
|
|
@@ -260,6 +366,58 @@ declare const flags: {
|
|
|
260
366
|
/** True once identify() has completed and flags are available. */
|
|
261
367
|
readonly ready: boolean;
|
|
262
368
|
};
|
|
369
|
+
interface SeeApi {
|
|
370
|
+
/**
|
|
371
|
+
* Report a handled problem and its product consequence:
|
|
372
|
+
*
|
|
373
|
+
* ```ts
|
|
374
|
+
* import { see } from "@shipeasy/sdk/client";
|
|
375
|
+
*
|
|
376
|
+
* try {
|
|
377
|
+
* await submitOrder(order);
|
|
378
|
+
* } catch (e) {
|
|
379
|
+
* see(e).causes_the("checkout").to("use cached prices").extras({ order_id: order.id });
|
|
380
|
+
* }
|
|
381
|
+
* ```
|
|
382
|
+
*
|
|
383
|
+
* The chain dispatches on the next microtask, so the report ships
|
|
384
|
+
* immediately after the statement (no `.send()` needed) into the errors
|
|
385
|
+
* primitive — grouped by fingerprint, near-real-time timeseries. If you
|
|
386
|
+
* don't know the consequence of an exception, don't catch it.
|
|
387
|
+
*/
|
|
388
|
+
(problem: unknown): SeeChain;
|
|
389
|
+
/**
|
|
390
|
+
* Report a non-exception problem. Prefer passing a caught Error to `see()`
|
|
391
|
+
* when one exists. The name is a stable identifier (it participates in the
|
|
392
|
+
* issue fingerprint) — variable data goes in `.message()` or `.extras()`.
|
|
393
|
+
*
|
|
394
|
+
* ```ts
|
|
395
|
+
* if (rows.length > LIMIT) {
|
|
396
|
+
* see.Violation("large query").message(`got ${rows.length} rows`)
|
|
397
|
+
* .causes_the("search results").to("be trimmed");
|
|
398
|
+
* }
|
|
399
|
+
* ```
|
|
400
|
+
*/
|
|
401
|
+
Violation(name: string): SeeViolationChain;
|
|
402
|
+
/**
|
|
403
|
+
* Mark an exception as expected control flow — auto-capture skips it and
|
|
404
|
+
* nothing is reported. The reason must start with "because".
|
|
405
|
+
*
|
|
406
|
+
* ```ts
|
|
407
|
+
* } catch (e) {
|
|
408
|
+
* see.ControlFlowException(e, "because the blob wasn't an encoded Foo");
|
|
409
|
+
* return decodeAsBar(blob);
|
|
410
|
+
* }
|
|
411
|
+
* ```
|
|
412
|
+
*/
|
|
413
|
+
ControlFlowException(err: unknown, because: string): void;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Structured error reporter — the whole grammar hangs off this one import.
|
|
417
|
+
* Safe to import anywhere; a call before `shipeasy({ clientKey })` warns and
|
|
418
|
+
* drops (never throws).
|
|
419
|
+
*/
|
|
420
|
+
declare const see: SeeApi;
|
|
263
421
|
declare const LABEL_MARKER_START = "\uFFF9";
|
|
264
422
|
declare const LABEL_MARKER_SEP = "\uFFFA";
|
|
265
423
|
declare const LABEL_MARKER_END = "\uFFFB";
|
|
@@ -298,4 +456,4 @@ interface I18nFacade {
|
|
|
298
456
|
}
|
|
299
457
|
declare const i18n: I18nFacade;
|
|
300
458
|
|
|
301
|
-
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 };
|
|
459
|
+
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 };
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,3 +1,62 @@
|
|
|
1
|
+
type SeeExtras = Record<string, string | number | boolean | null | undefined>;
|
|
2
|
+
type SeeKind = "caught" | "uncaught" | "unhandled_rejection" | "network" | "violation";
|
|
3
|
+
/** Built by `causesThe(subject).to(outcome)` — never constructed by hand. */
|
|
4
|
+
interface Consequence {
|
|
5
|
+
readonly __seConsequence: true;
|
|
6
|
+
readonly subject: string;
|
|
7
|
+
readonly outcome: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Non-exception problem, built by `violation(name)`. A plain branded object
|
|
11
|
+
* (not an Error subclass) so `.message()` can be a builder method without
|
|
12
|
+
* colliding with `Error.prototype.message`.
|
|
13
|
+
*/
|
|
14
|
+
interface Violation {
|
|
15
|
+
readonly __seViolation: true;
|
|
16
|
+
readonly violationName: string;
|
|
17
|
+
readonly violationMessage?: string;
|
|
18
|
+
/** Attach free-form detail. Variable data goes HERE (or in extras), never in the name. */
|
|
19
|
+
message(msg: string): Violation;
|
|
20
|
+
}
|
|
21
|
+
/** Wire shape — the `type:"error"` RawEvent variant accepted by POST /collect. */
|
|
22
|
+
interface SeeErrorEvent {
|
|
23
|
+
type: "error";
|
|
24
|
+
kind: SeeKind;
|
|
25
|
+
/** Error class/name (e.g. "TypeError") or the violation name. */
|
|
26
|
+
error_type: string;
|
|
27
|
+
message: string;
|
|
28
|
+
stack?: string;
|
|
29
|
+
/** Consequence: "<error_type> causes the <subject> to <outcome>". */
|
|
30
|
+
subject: string;
|
|
31
|
+
outcome: string;
|
|
32
|
+
extras?: Record<string, string | number | boolean>;
|
|
33
|
+
url?: string;
|
|
34
|
+
user_id?: string;
|
|
35
|
+
anonymous_id?: string;
|
|
36
|
+
side: "client" | "server";
|
|
37
|
+
env?: string;
|
|
38
|
+
sdk_version: string;
|
|
39
|
+
ts: number;
|
|
40
|
+
}
|
|
41
|
+
interface SeeExtrasTail {
|
|
42
|
+
/** Attach debugging metadata. Callable repeatedly — keys merge, later wins. */
|
|
43
|
+
extras(extras: SeeExtras): SeeExtrasTail;
|
|
44
|
+
}
|
|
45
|
+
interface SeeOutcomeStep {
|
|
46
|
+
/** The user-visible impact: `.causes_the("checkout").to("use cached prices")`. */
|
|
47
|
+
to(outcome: string): SeeExtrasTail;
|
|
48
|
+
}
|
|
49
|
+
interface SeeChain {
|
|
50
|
+
/** Start the consequence sentence — the product surface affected. */
|
|
51
|
+
causes_the(subject: string): SeeOutcomeStep;
|
|
52
|
+
/** camelCase alias of {@link SeeChain.causes_the}. */
|
|
53
|
+
causesThe(subject: string): SeeOutcomeStep;
|
|
54
|
+
}
|
|
55
|
+
interface SeeViolationChain extends SeeChain {
|
|
56
|
+
/** Free-form detail. Variable data goes here (or extras), never in the name. */
|
|
57
|
+
message(msg: string): SeeViolationChain;
|
|
58
|
+
}
|
|
59
|
+
|
|
1
60
|
declare global {
|
|
2
61
|
interface Window {
|
|
3
62
|
i18n?: {
|
|
@@ -8,7 +67,7 @@ declare global {
|
|
|
8
67
|
};
|
|
9
68
|
}
|
|
10
69
|
}
|
|
11
|
-
declare const version = "
|
|
70
|
+
declare const version = "4.0.0";
|
|
12
71
|
interface User {
|
|
13
72
|
user_id?: string;
|
|
14
73
|
[attr: string]: unknown;
|
|
@@ -51,19 +110,38 @@ interface FlagsClientBrowserOptions {
|
|
|
51
110
|
* true when `autoGuardrails` is true).
|
|
52
111
|
*/
|
|
53
112
|
autoGuardrailGroups?: Partial<AutoCollectGroups>;
|
|
113
|
+
/**
|
|
114
|
+
* Emit `__auto_*` metric events for ALL visitors, not just experiment
|
|
115
|
+
* participants. Default false: auto-metrics are gated on the visitor having
|
|
116
|
+
* seen ≥1 experiment exposure (the only data the analysis pipeline reads;
|
|
117
|
+
* ungated emission is pure AE write cost at scale — see cost.md).
|
|
118
|
+
*/
|
|
119
|
+
autoCollectAlways?: boolean;
|
|
54
120
|
/** Which published env to read values from. Defaults to "prod". */
|
|
55
121
|
env?: FlagsClientBrowserEnv;
|
|
122
|
+
/**
|
|
123
|
+
* Per-evaluation usage telemetry. ON by default — each getFlag/getConfig/
|
|
124
|
+
* getExperiment/getKillswitch call fires one fire-and-forget sendBeacon so
|
|
125
|
+
* usage is counted by Cloudflare's native per-path analytics. Pass `true` to
|
|
126
|
+
* disable entirely.
|
|
127
|
+
*/
|
|
128
|
+
disableTelemetry?: boolean;
|
|
129
|
+
/** Override the telemetry beacon host. Defaults to {@link DEFAULT_TELEMETRY_URL}. */
|
|
130
|
+
telemetryUrl?: string;
|
|
56
131
|
}
|
|
57
132
|
declare class FlagsClientBrowser {
|
|
58
133
|
private readonly sdkKey;
|
|
59
134
|
private readonly baseUrl;
|
|
60
135
|
private readonly autoGuardrails;
|
|
61
136
|
private readonly autoGuardrailGroups;
|
|
137
|
+
private readonly autoCollectAlways;
|
|
62
138
|
private readonly env;
|
|
63
139
|
private evalResult;
|
|
64
140
|
private anonId;
|
|
65
141
|
private userId;
|
|
66
142
|
private buffer;
|
|
143
|
+
private telemetry;
|
|
144
|
+
private seeLimiter;
|
|
67
145
|
private guardrailsInstalled;
|
|
68
146
|
private listeners;
|
|
69
147
|
private overrideListenerInstalled;
|
|
@@ -71,6 +149,12 @@ declare class FlagsClientBrowser {
|
|
|
71
149
|
private onOverrideChange;
|
|
72
150
|
constructor(opts: FlagsClientBrowserOptions);
|
|
73
151
|
identify(user: User): Promise<void>;
|
|
152
|
+
/**
|
|
153
|
+
* Report a structured error into the errors primitive. Flushes immediately
|
|
154
|
+
* (beacon-first) — error occurrences are near-real-time, never queued behind
|
|
155
|
+
* the 5s metric batch. Spam-guarded by a 30s dedup window + per-session cap.
|
|
156
|
+
*/
|
|
157
|
+
reportError(problem: unknown, consequence: Consequence, extras?: SeeExtras, kind?: SeeKind): void;
|
|
74
158
|
get ready(): boolean;
|
|
75
159
|
private notify;
|
|
76
160
|
initFromBootstrap(data: EvalResponse): void;
|
|
@@ -161,10 +245,11 @@ interface ShipeasyClientConfig {
|
|
|
161
245
|
*/
|
|
162
246
|
autoIdentify?: boolean;
|
|
163
247
|
/**
|
|
164
|
-
* Capture web vitals (LCP, CLS, INP, TTFB, FCP, navigation timing)
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
248
|
+
* Capture web vitals (LCP, CLS, INP, TTFB, FCP, navigation timing) and
|
|
249
|
+
* engagement signals (abandonment) as `__auto_*` metric events, plus JS /
|
|
250
|
+
* network errors as structured error events in the errors primitive (same
|
|
251
|
+
* pipeline as `see()` — grouped by fingerprint, near-real-time). Defaults
|
|
252
|
+
* to `true`.
|
|
168
253
|
*
|
|
169
254
|
* Pass `false` to disable everything, or a per-group object to narrow:
|
|
170
255
|
*
|
|
@@ -173,8 +258,29 @@ interface ShipeasyClientConfig {
|
|
|
173
258
|
* shipeasy({ clientKey, autoCollect: { errors: false } }); // vitals + engagement only
|
|
174
259
|
* shipeasy({ clientKey }); // all groups on
|
|
175
260
|
* ```
|
|
261
|
+
*
|
|
262
|
+
* Since 4.1.0, `__auto_*` metric events are only emitted for visitors who
|
|
263
|
+
* are in ≥1 active experiment (the analysis pipeline reads nothing else;
|
|
264
|
+
* gating cuts the dominant Analytics Engine write cost at scale). Pass
|
|
265
|
+
* `{ always: true }` to collect site-wide vitals regardless of experiment
|
|
266
|
+
* participation:
|
|
267
|
+
*
|
|
268
|
+
* ```ts
|
|
269
|
+
* shipeasy({ clientKey, autoCollect: { always: true } }); // site-wide vitals
|
|
270
|
+
* ```
|
|
271
|
+
*
|
|
272
|
+
* `__auto_abandoned` is always emitted (it fires when the user leaves
|
|
273
|
+
* before exposures could land) and error capture is unaffected.
|
|
176
274
|
*/
|
|
177
|
-
autoCollect?: boolean | Partial<AutoCollectGroups
|
|
275
|
+
autoCollect?: boolean | (Partial<AutoCollectGroups> & {
|
|
276
|
+
always?: boolean;
|
|
277
|
+
});
|
|
278
|
+
/**
|
|
279
|
+
* Disable per-evaluation usage telemetry. Telemetry is ON by default — every
|
|
280
|
+
* flag/config/experiment/killswitch read fires one fire-and-forget beacon
|
|
281
|
+
* counted by Cloudflare's native per-path analytics. Pass `true` to opt out.
|
|
282
|
+
*/
|
|
283
|
+
disableTelemetry?: boolean;
|
|
178
284
|
}
|
|
179
285
|
/**
|
|
180
286
|
* Initialise the ShipEasy client SDK and wire up lazy devtools.
|
|
@@ -260,6 +366,58 @@ declare const flags: {
|
|
|
260
366
|
/** True once identify() has completed and flags are available. */
|
|
261
367
|
readonly ready: boolean;
|
|
262
368
|
};
|
|
369
|
+
interface SeeApi {
|
|
370
|
+
/**
|
|
371
|
+
* Report a handled problem and its product consequence:
|
|
372
|
+
*
|
|
373
|
+
* ```ts
|
|
374
|
+
* import { see } from "@shipeasy/sdk/client";
|
|
375
|
+
*
|
|
376
|
+
* try {
|
|
377
|
+
* await submitOrder(order);
|
|
378
|
+
* } catch (e) {
|
|
379
|
+
* see(e).causes_the("checkout").to("use cached prices").extras({ order_id: order.id });
|
|
380
|
+
* }
|
|
381
|
+
* ```
|
|
382
|
+
*
|
|
383
|
+
* The chain dispatches on the next microtask, so the report ships
|
|
384
|
+
* immediately after the statement (no `.send()` needed) into the errors
|
|
385
|
+
* primitive — grouped by fingerprint, near-real-time timeseries. If you
|
|
386
|
+
* don't know the consequence of an exception, don't catch it.
|
|
387
|
+
*/
|
|
388
|
+
(problem: unknown): SeeChain;
|
|
389
|
+
/**
|
|
390
|
+
* Report a non-exception problem. Prefer passing a caught Error to `see()`
|
|
391
|
+
* when one exists. The name is a stable identifier (it participates in the
|
|
392
|
+
* issue fingerprint) — variable data goes in `.message()` or `.extras()`.
|
|
393
|
+
*
|
|
394
|
+
* ```ts
|
|
395
|
+
* if (rows.length > LIMIT) {
|
|
396
|
+
* see.Violation("large query").message(`got ${rows.length} rows`)
|
|
397
|
+
* .causes_the("search results").to("be trimmed");
|
|
398
|
+
* }
|
|
399
|
+
* ```
|
|
400
|
+
*/
|
|
401
|
+
Violation(name: string): SeeViolationChain;
|
|
402
|
+
/**
|
|
403
|
+
* Mark an exception as expected control flow — auto-capture skips it and
|
|
404
|
+
* nothing is reported. The reason must start with "because".
|
|
405
|
+
*
|
|
406
|
+
* ```ts
|
|
407
|
+
* } catch (e) {
|
|
408
|
+
* see.ControlFlowException(e, "because the blob wasn't an encoded Foo");
|
|
409
|
+
* return decodeAsBar(blob);
|
|
410
|
+
* }
|
|
411
|
+
* ```
|
|
412
|
+
*/
|
|
413
|
+
ControlFlowException(err: unknown, because: string): void;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Structured error reporter — the whole grammar hangs off this one import.
|
|
417
|
+
* Safe to import anywhere; a call before `shipeasy({ clientKey })` warns and
|
|
418
|
+
* drops (never throws).
|
|
419
|
+
*/
|
|
420
|
+
declare const see: SeeApi;
|
|
263
421
|
declare const LABEL_MARKER_START = "\uFFF9";
|
|
264
422
|
declare const LABEL_MARKER_SEP = "\uFFFA";
|
|
265
423
|
declare const LABEL_MARKER_END = "\uFFFB";
|
|
@@ -298,4 +456,4 @@ interface I18nFacade {
|
|
|
298
456
|
}
|
|
299
457
|
declare const i18n: I18nFacade;
|
|
300
458
|
|
|
301
|
-
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 };
|
|
459
|
+
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 };
|