@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.
@@ -1,4 +1,63 @@
1
- declare const version = "1.0.0";
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
+
60
+ declare const version = "4.0.0";
2
61
  interface User {
3
62
  user_id?: string;
4
63
  anonymous_id?: string;
@@ -51,11 +110,24 @@ interface FlagsClientOptions {
51
110
  * for tests; production callers should rely on init()/initOnce().
52
111
  */
53
112
  initialBlob?: FlagsBlob;
113
+ /**
114
+ * Per-evaluation usage telemetry. ON by default — each getFlag/getConfig/
115
+ * getExperiment/getKillswitch (and the per-key evaluate() loop) fires one
116
+ * fire-and-forget beacon counted by Cloudflare's native per-path analytics.
117
+ * Pass `true` to disable. NOTE: on Cloudflare Workers each beacon is an
118
+ * outbound subrequest (cap 50 free / 1000 paid per invocation), so disable
119
+ * this on hot request paths that evaluate many flags per request.
120
+ */
121
+ disableTelemetry?: boolean;
122
+ /** Override the telemetry beacon host. Defaults to {@link DEFAULT_TELEMETRY_URL}. */
123
+ telemetryUrl?: string;
54
124
  }
55
125
  declare class FlagsClient {
56
126
  private readonly apiKey;
57
127
  private readonly baseUrl;
58
128
  private readonly env;
129
+ private readonly telemetry;
130
+ private readonly seeLimiter;
59
131
  private flagsBlob;
60
132
  private expsBlob;
61
133
  private flagsEtag;
@@ -75,6 +147,12 @@ declare class FlagsClient {
75
147
  getConfig<T = unknown>(name: string, decode?: (raw: unknown) => T): T | undefined;
76
148
  getExperiment<P extends Record<string, unknown>>(name: string, user: User, defaultParams: P, decode?: (raw: unknown) => P): ExperimentResult<P>;
77
149
  track(userId: string, eventName: string, props?: Record<string, unknown>): void;
150
+ /**
151
+ * Report a structured error into the errors primitive. Fire-and-forget —
152
+ * never blocks or throws into the request path. Spam-guarded by a 30s
153
+ * dedup window + per-process cap.
154
+ */
155
+ reportError(problem: unknown, consequence: Consequence, extras?: SeeExtras, kind?: SeeKind): void;
78
156
  /**
79
157
  * Evaluate all flags, configs, and experiments for a user against the locally
80
158
  * cached blob (no network call). Applies ?se_ks_* / ?se_cf_* / ?se_exp_*
@@ -147,6 +225,12 @@ interface ShipeasyServerConfig {
147
225
  user?: User;
148
226
  /** i18n profile to load for SSR translations, e.g. "en:prod". Defaults to "en:prod". */
149
227
  i18nDefaultProfile?: string;
228
+ /**
229
+ * Disable per-evaluation usage telemetry. ON by default. On Cloudflare
230
+ * Workers each beacon is an outbound subrequest, so disable on hot SSR paths
231
+ * that evaluate many flags per request. See {@link FlagsClientOptions.disableTelemetry}.
232
+ */
233
+ disableTelemetry?: boolean;
150
234
  }
151
235
  interface ShipeasyServerHandle {
152
236
  flags: Record<string, boolean>;
@@ -211,5 +295,49 @@ declare const flags: {
211
295
  */
212
296
  evaluate(user: User, rawUrl?: string): BootstrapPayload;
213
297
  };
298
+ interface SeeApi {
299
+ /**
300
+ * Report a handled problem and its product consequence:
301
+ *
302
+ * ```ts
303
+ * import { see } from "@shipeasy/sdk/server";
304
+ *
305
+ * try {
306
+ * await chargeCard(order);
307
+ * } catch (e) {
308
+ * see(e).causes_the("payment").to("use the backup processor").extras({ order_id: order.id });
309
+ * await chargeViaBackup(order);
310
+ * }
311
+ * ```
312
+ *
313
+ * The chain dispatches on the next microtask — fire-and-forget into the
314
+ * errors primitive (grouped by fingerprint, near-real-time timeseries).
315
+ * If you don't know the consequence of an exception, don't catch it.
316
+ */
317
+ (problem: unknown): SeeChain;
318
+ /**
319
+ * Report a non-exception problem. Prefer passing a caught Error to `see()`
320
+ * when one exists. The name is a stable identifier (it participates in the
321
+ * issue fingerprint) — variable data goes in `.message()` or `.extras()`.
322
+ *
323
+ * ```ts
324
+ * if (results.length > LIMIT) {
325
+ * see.Violation("large query").causes_the("search results").to("be trimmed");
326
+ * }
327
+ * ```
328
+ */
329
+ Violation(name: string): SeeViolationChain;
330
+ /**
331
+ * Mark an exception as expected control flow — documents the expectation and
332
+ * reports nothing. The reason must start with "because".
333
+ */
334
+ ControlFlowException(err: unknown, because: string): void;
335
+ }
336
+ /**
337
+ * Structured error reporter — the whole grammar hangs off this one import.
338
+ * Safe to import anywhere; a call before `shipeasy({ serverKey })` warns and
339
+ * drops (never throws).
340
+ */
341
+ declare const see: SeeApi;
214
342
 
215
- export { type BootstrapHtmlOptions, type BootstrapPayload, type ExperimentResult, type FetchLabelsOptions, FlagsClient, type FlagsClientEnv, type FlagsClientOptions, type I18nForRequest, type LabelFile, type ShipeasyServerConfig, type ShipeasyServerHandle, type User, _resetShipeasyServerForTests, configureShipeasyServer, fetchLabelsForSSR, flags, getBootstrapHtml, getShipeasyServerClient, i18n, shipeasy, version };
343
+ 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 };
@@ -1,4 +1,63 @@
1
- declare const version = "1.0.0";
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
+
60
+ declare const version = "4.0.0";
2
61
  interface User {
3
62
  user_id?: string;
4
63
  anonymous_id?: string;
@@ -51,11 +110,24 @@ interface FlagsClientOptions {
51
110
  * for tests; production callers should rely on init()/initOnce().
52
111
  */
53
112
  initialBlob?: FlagsBlob;
113
+ /**
114
+ * Per-evaluation usage telemetry. ON by default — each getFlag/getConfig/
115
+ * getExperiment/getKillswitch (and the per-key evaluate() loop) fires one
116
+ * fire-and-forget beacon counted by Cloudflare's native per-path analytics.
117
+ * Pass `true` to disable. NOTE: on Cloudflare Workers each beacon is an
118
+ * outbound subrequest (cap 50 free / 1000 paid per invocation), so disable
119
+ * this on hot request paths that evaluate many flags per request.
120
+ */
121
+ disableTelemetry?: boolean;
122
+ /** Override the telemetry beacon host. Defaults to {@link DEFAULT_TELEMETRY_URL}. */
123
+ telemetryUrl?: string;
54
124
  }
55
125
  declare class FlagsClient {
56
126
  private readonly apiKey;
57
127
  private readonly baseUrl;
58
128
  private readonly env;
129
+ private readonly telemetry;
130
+ private readonly seeLimiter;
59
131
  private flagsBlob;
60
132
  private expsBlob;
61
133
  private flagsEtag;
@@ -75,6 +147,12 @@ declare class FlagsClient {
75
147
  getConfig<T = unknown>(name: string, decode?: (raw: unknown) => T): T | undefined;
76
148
  getExperiment<P extends Record<string, unknown>>(name: string, user: User, defaultParams: P, decode?: (raw: unknown) => P): ExperimentResult<P>;
77
149
  track(userId: string, eventName: string, props?: Record<string, unknown>): void;
150
+ /**
151
+ * Report a structured error into the errors primitive. Fire-and-forget —
152
+ * never blocks or throws into the request path. Spam-guarded by a 30s
153
+ * dedup window + per-process cap.
154
+ */
155
+ reportError(problem: unknown, consequence: Consequence, extras?: SeeExtras, kind?: SeeKind): void;
78
156
  /**
79
157
  * Evaluate all flags, configs, and experiments for a user against the locally
80
158
  * cached blob (no network call). Applies ?se_ks_* / ?se_cf_* / ?se_exp_*
@@ -147,6 +225,12 @@ interface ShipeasyServerConfig {
147
225
  user?: User;
148
226
  /** i18n profile to load for SSR translations, e.g. "en:prod". Defaults to "en:prod". */
149
227
  i18nDefaultProfile?: string;
228
+ /**
229
+ * Disable per-evaluation usage telemetry. ON by default. On Cloudflare
230
+ * Workers each beacon is an outbound subrequest, so disable on hot SSR paths
231
+ * that evaluate many flags per request. See {@link FlagsClientOptions.disableTelemetry}.
232
+ */
233
+ disableTelemetry?: boolean;
150
234
  }
151
235
  interface ShipeasyServerHandle {
152
236
  flags: Record<string, boolean>;
@@ -211,5 +295,49 @@ declare const flags: {
211
295
  */
212
296
  evaluate(user: User, rawUrl?: string): BootstrapPayload;
213
297
  };
298
+ interface SeeApi {
299
+ /**
300
+ * Report a handled problem and its product consequence:
301
+ *
302
+ * ```ts
303
+ * import { see } from "@shipeasy/sdk/server";
304
+ *
305
+ * try {
306
+ * await chargeCard(order);
307
+ * } catch (e) {
308
+ * see(e).causes_the("payment").to("use the backup processor").extras({ order_id: order.id });
309
+ * await chargeViaBackup(order);
310
+ * }
311
+ * ```
312
+ *
313
+ * The chain dispatches on the next microtask — fire-and-forget into the
314
+ * errors primitive (grouped by fingerprint, near-real-time timeseries).
315
+ * If you don't know the consequence of an exception, don't catch it.
316
+ */
317
+ (problem: unknown): SeeChain;
318
+ /**
319
+ * Report a non-exception problem. Prefer passing a caught Error to `see()`
320
+ * when one exists. The name is a stable identifier (it participates in the
321
+ * issue fingerprint) — variable data goes in `.message()` or `.extras()`.
322
+ *
323
+ * ```ts
324
+ * if (results.length > LIMIT) {
325
+ * see.Violation("large query").causes_the("search results").to("be trimmed");
326
+ * }
327
+ * ```
328
+ */
329
+ Violation(name: string): SeeViolationChain;
330
+ /**
331
+ * Mark an exception as expected control flow — documents the expectation and
332
+ * reports nothing. The reason must start with "because".
333
+ */
334
+ ControlFlowException(err: unknown, because: string): void;
335
+ }
336
+ /**
337
+ * Structured error reporter — the whole grammar hangs off this one import.
338
+ * Safe to import anywhere; a call before `shipeasy({ serverKey })` warns and
339
+ * drops (never throws).
340
+ */
341
+ declare const see: SeeApi;
214
342
 
215
- export { type BootstrapHtmlOptions, type BootstrapPayload, type ExperimentResult, type FetchLabelsOptions, FlagsClient, type FlagsClientEnv, type FlagsClientOptions, type I18nForRequest, type LabelFile, type ShipeasyServerConfig, type ShipeasyServerHandle, type User, _resetShipeasyServerForTests, configureShipeasyServer, fetchLabelsForSSR, flags, getBootstrapHtml, getShipeasyServerClient, i18n, shipeasy, version };
343
+ 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 };