@shipeasy/sdk 6.0.0 → 6.3.1

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,5 +1,9 @@
1
1
  import { AsyncLocalStorage } from 'node:async_hooks';
2
2
 
3
+ type LogLevel = "silent" | "error" | "warn" | "info" | "debug";
4
+ /** All accepted {@link LogLevel} values, in increasing verbosity. */
5
+ declare const LOG_LEVELS: readonly LogLevel[];
6
+
3
7
  type SeeExtras = Record<string, string | number | boolean | null | undefined>;
4
8
  type SeeKind = "caught" | "uncaught" | "unhandled_rejection" | "network" | "violation";
5
9
  /** Built by `causesThe(subject).to(outcome)` — never constructed by hand. */
@@ -243,6 +247,16 @@ interface EngineOptions {
243
247
  disableTelemetry?: boolean;
244
248
  /** Override the telemetry beacon host. Defaults to {@link DEFAULT_TELEMETRY_URL}. */
245
249
  telemetryUrl?: string;
250
+ /**
251
+ * How chatty the SDK is on `console` when it catches an error internally.
252
+ * Every public runtime method (getFlag/getConfig/getExperiment/getKillswitch/
253
+ * track/logExposure/see) fails silently — it returns a safe default rather
254
+ * than throwing — and surfaces the swallowed error through this level so you
255
+ * still find out. Ordering: `silent` < `error` < `warn` < `info` < `debug`.
256
+ * Defaults to `"warn"` (prints `error` + `warn`). Pass `"silent"` to mute the
257
+ * SDK entirely. See {@link LogLevel}.
258
+ */
259
+ logLevel?: LogLevel;
246
260
  /**
247
261
  * Attribute names usable for targeting but never persisted in analytics
248
262
  * (LD/Statsig `privateAttributes`). The server evaluates locally so private
@@ -460,6 +474,13 @@ interface ShipeasyServerConfig {
460
474
  user?: User;
461
475
  /** i18n profile to load for SSR translations, e.g. "en:prod". Defaults to "en:prod". */
462
476
  i18nDefaultProfile?: string;
477
+ /**
478
+ * How chatty the SDK is on `console` when it swallows an internal error.
479
+ * `silent` < `error` < `warn` < `info` < `debug`; defaults to `"warn"`. Every
480
+ * runtime read/track/see call fails silently to a safe default and reports the
481
+ * cause at this level. See {@link EngineOptions.logLevel}.
482
+ */
483
+ logLevel?: LogLevel;
463
484
  /**
464
485
  * Disable per-evaluation usage telemetry. ON by default. On Cloudflare
465
486
  * Workers each beacon is an outbound subrequest, so disable on hot SSR paths
@@ -591,6 +612,20 @@ interface ConfigureOptions<U = unknown> extends Omit<EngineOptions, "apiKey"> {
591
612
  * verbatim, so it should carry `user_id`/`anonymous_id` + any targeting attrs).
592
613
  */
593
614
  attributes?: AttributesFn<U>;
615
+ /**
616
+ * Long-running server: start the **background poll** internally (initial fetch
617
+ * + periodic refresh) so flags stay fresh without a redeploy. Default `false`.
618
+ * With `poll: false` (default) a one-shot fetch is kicked off fire-and-forget
619
+ * (serverless-friendly). You never need to call `engine.init()` yourself —
620
+ * configuration owns the lifecycle.
621
+ */
622
+ poll?: boolean;
623
+ /**
624
+ * One-shot fetch on configure (fire-and-forget). Default `true`. Ignored when
625
+ * `poll: true` (the poll does the initial fetch). Set `false` only if you want
626
+ * to control the first fetch yourself.
627
+ */
628
+ init?: boolean;
594
629
  }
595
630
  /**
596
631
  * Configure the SDK once at app boot, then evaluate per user with
@@ -613,6 +648,77 @@ interface ConfigureOptions<U = unknown> extends Omit<EngineOptions, "apiKey"> {
613
648
  declare function configure<U = unknown>(opts: ConfigureOptions<U>): Engine;
614
649
  /** Test seam: reset the registered attribute transform. */
615
650
  declare function _resetConfigureForTests(): void;
651
+ /** Seed shapes shared by {@link configureForTesting} / {@link configureForOffline}. */
652
+ interface ConfigureTestOptions<U = unknown> {
653
+ /** Same transform as {@link configure} (default identity). */
654
+ attributes?: AttributesFn<U>;
655
+ /** `{ name: bool }` — forced `getFlag` results. */
656
+ flags?: Record<string, boolean>;
657
+ /** `{ name: value }` — forced `getConfig` results. */
658
+ configs?: Record<string, unknown>;
659
+ /** `{ name: [group, params] }` — forced enrolments. */
660
+ experiments?: Record<string, [string, Record<string, unknown>]>;
661
+ }
662
+ /**
663
+ * Configure the SDK in **test mode** — a drop-in sibling of {@link configure}
664
+ * with no network, ever (no api key needed). Seed what your code under test
665
+ * should see, then read through the ordinary `new Client(user)`:
666
+ *
667
+ * ```ts
668
+ * configureForTesting({ flags: { new_checkout: true } });
669
+ * const flags = new Client({ user_id: "u_1" });
670
+ * flags.getFlag("new_checkout"); // true
671
+ * ```
672
+ *
673
+ * Replaces any previously-configured engine, so tests can reconfigure freely.
674
+ */
675
+ declare function configureForTesting<U = unknown>(opts?: ConfigureTestOptions<U>): Engine;
676
+ /** Options for {@link configureForOffline} — exactly one of `snapshot` / `path`. */
677
+ interface ConfigureOfflineOptions<U = unknown> extends ConfigureTestOptions<U> {
678
+ /** In-memory `{ flags: <body of /sdk/flags>, experiments: <body of /sdk/experiments> }`. */
679
+ snapshot?: {
680
+ flags: FlagsBlob;
681
+ experiments: ExpsBlob;
682
+ };
683
+ /** Path to a JSON file `{ flags, experiments }`. */
684
+ path?: string;
685
+ }
686
+ /**
687
+ * Configure the SDK **offline** — evaluate the REAL rules from an in-memory
688
+ * snapshot or a JSON file, with no network. A drop-in sibling of
689
+ * {@link configure} (no api key needed). Optional `flags`/`configs`/`experiments`
690
+ * overrides are layered on top (same shapes as {@link configureForTesting}).
691
+ * Replaces any previously-configured engine.
692
+ */
693
+ declare function configureForOffline<U = unknown>(opts: ConfigureOfflineOptions<U>): Engine;
694
+ /**
695
+ * Force `getFlag(name)` → `value` on the spot, for the current config. A quick
696
+ * in-test override layered on top of whatever {@link configureForTesting} /
697
+ * {@link configureForOffline} (or {@link configure}) set up — wins over the blob
698
+ * until {@link clearOverrides}.
699
+ */
700
+ declare function overrideFlag(name: string, value: boolean): void;
701
+ /** Force `getConfig(name)` → `value` on the spot (see {@link overrideFlag}). */
702
+ declare function overrideConfig(name: string, value: unknown): void;
703
+ /**
704
+ * Force `getExperiment(name)` to report enrolment in `group` with `params` on
705
+ * the spot (see {@link overrideFlag}).
706
+ */
707
+ declare function overrideExperiment(name: string, group: string, params: Record<string, unknown>): void;
708
+ /**
709
+ * Drop every on-the-spot flag/config/experiment override — INCLUDING the seed
710
+ * from {@link configureForTesting} (test mode has no blob beneath, so everything
711
+ * reverts to the empty-blob defaults). Under {@link configureForOffline} the
712
+ * snapshot remains and evaluations revert to it.
713
+ */
714
+ declare function clearOverrides(): void;
715
+ /**
716
+ * Register a listener fired after a background poll fetches NEW data (a 200, not
717
+ * a 304). Returns an unsubscribe callable. Requires `configure({ poll: true })`
718
+ * (no poll thread runs otherwise). Configuration owns the engine; you never
719
+ * touch it.
720
+ */
721
+ declare function onChange(listener: () => void): () => void;
616
722
  /**
617
723
  * A user-bound evaluation handle. Construct one per user/request — it's cheap
618
724
  * (it delegates to the {@link Engine} built by {@link configure}); it does NOT
@@ -637,6 +743,21 @@ declare class Client<U = unknown> {
637
743
  getExperiment<P extends Record<string, unknown>>(name: string, defaultParams: P, decode?: (raw: unknown) => P): ExperimentResult<P>;
638
744
  /** Read a killswitch (not user-bound; mirrors {@link Engine.getKillswitch}). */
639
745
  getKillswitch(name: string, switchKey?: string): boolean;
746
+ /**
747
+ * Record a conversion/metric event for the bound user. Derives the unit from
748
+ * the resolved attribute bag (`user_id`, else `anonymous_id`) and delegates to
749
+ * {@link Engine.track} — so an experiment is end-to-end Client-only (no need to
750
+ * drop down to the Engine to log a conversion). Fire-and-forget; no-op in test
751
+ * mode.
752
+ */
753
+ track(eventName: string, props?: Record<string, unknown>): void;
754
+ /**
755
+ * Emit an exposure event for `name` at this server-side decision point for the
756
+ * bound user. Delegates to {@link Engine.logExposure} with the resolved
757
+ * attribute bag (re-evaluates enrolment; no-op when the user isn't enrolled or
758
+ * in test mode).
759
+ */
760
+ logExposure(name: string): void;
640
761
  }
641
762
  interface SeeApi {
642
763
  /**
@@ -689,4 +810,4 @@ interface SeeApi {
689
810
  */
690
811
  declare const see: SeeApi;
691
812
 
692
- export { ANON_ID_COOKIE, type AttributesFn, type BootstrapData, type BootstrapEmitOptions, type BootstrapPayload, Client, type ConfigureOptions, type Consequence, Engine, type EngineEnv, type EngineOptions, type ExperimentResult, type ExpsBlob, FLAG_REASONS, type FetchLabelsOptions, type FlagDetail, type FlagReason, type FlagsBlob, type GetConfigOptions, type I18nForRequest, type LabelFile, type ScriptTagSpec, type SeeApi, type SeeChain, type SeeControlFlowChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyServerConfig, type ShipeasyServerHandle, type StickyBucketStore, type StickyEntry, type User, type Violation, _murmur3ForTests, _resetConfigureForTests, _resetShipeasyServerForTests, configure, configureShipeasyServer, createInMemoryStickyStore, fetchLabelsForSSR, flags, getBootstrapData, getBootstrapTags, getShipeasyServerClient, i18n, isExpected, see, seeContext, shipeasy, version };
813
+ export { ANON_ID_COOKIE, type AttributesFn, type BootstrapData, type BootstrapEmitOptions, type BootstrapPayload, Client, type ConfigureOfflineOptions, type ConfigureOptions, type ConfigureTestOptions, type Consequence, Engine, type EngineEnv, type EngineOptions, type ExperimentResult, type ExpsBlob, FLAG_REASONS, type FetchLabelsOptions, type FlagDetail, type FlagReason, type FlagsBlob, type GetConfigOptions, type I18nForRequest, LOG_LEVELS, type LabelFile, type LogLevel, type ScriptTagSpec, type SeeApi, type SeeChain, type SeeControlFlowChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyServerConfig, type ShipeasyServerHandle, type StickyBucketStore, type StickyEntry, type User, type Violation, _murmur3ForTests, _resetConfigureForTests, _resetShipeasyServerForTests, clearOverrides, configure, configureForOffline, configureForTesting, configureShipeasyServer, createInMemoryStickyStore, fetchLabelsForSSR, flags, getBootstrapData, getBootstrapTags, getShipeasyServerClient, i18n, isExpected, onChange, overrideConfig, overrideExperiment, overrideFlag, see, seeContext, shipeasy, version };
@@ -1,5 +1,9 @@
1
1
  import { AsyncLocalStorage } from 'node:async_hooks';
2
2
 
3
+ type LogLevel = "silent" | "error" | "warn" | "info" | "debug";
4
+ /** All accepted {@link LogLevel} values, in increasing verbosity. */
5
+ declare const LOG_LEVELS: readonly LogLevel[];
6
+
3
7
  type SeeExtras = Record<string, string | number | boolean | null | undefined>;
4
8
  type SeeKind = "caught" | "uncaught" | "unhandled_rejection" | "network" | "violation";
5
9
  /** Built by `causesThe(subject).to(outcome)` — never constructed by hand. */
@@ -243,6 +247,16 @@ interface EngineOptions {
243
247
  disableTelemetry?: boolean;
244
248
  /** Override the telemetry beacon host. Defaults to {@link DEFAULT_TELEMETRY_URL}. */
245
249
  telemetryUrl?: string;
250
+ /**
251
+ * How chatty the SDK is on `console` when it catches an error internally.
252
+ * Every public runtime method (getFlag/getConfig/getExperiment/getKillswitch/
253
+ * track/logExposure/see) fails silently — it returns a safe default rather
254
+ * than throwing — and surfaces the swallowed error through this level so you
255
+ * still find out. Ordering: `silent` < `error` < `warn` < `info` < `debug`.
256
+ * Defaults to `"warn"` (prints `error` + `warn`). Pass `"silent"` to mute the
257
+ * SDK entirely. See {@link LogLevel}.
258
+ */
259
+ logLevel?: LogLevel;
246
260
  /**
247
261
  * Attribute names usable for targeting but never persisted in analytics
248
262
  * (LD/Statsig `privateAttributes`). The server evaluates locally so private
@@ -460,6 +474,13 @@ interface ShipeasyServerConfig {
460
474
  user?: User;
461
475
  /** i18n profile to load for SSR translations, e.g. "en:prod". Defaults to "en:prod". */
462
476
  i18nDefaultProfile?: string;
477
+ /**
478
+ * How chatty the SDK is on `console` when it swallows an internal error.
479
+ * `silent` < `error` < `warn` < `info` < `debug`; defaults to `"warn"`. Every
480
+ * runtime read/track/see call fails silently to a safe default and reports the
481
+ * cause at this level. See {@link EngineOptions.logLevel}.
482
+ */
483
+ logLevel?: LogLevel;
463
484
  /**
464
485
  * Disable per-evaluation usage telemetry. ON by default. On Cloudflare
465
486
  * Workers each beacon is an outbound subrequest, so disable on hot SSR paths
@@ -591,6 +612,20 @@ interface ConfigureOptions<U = unknown> extends Omit<EngineOptions, "apiKey"> {
591
612
  * verbatim, so it should carry `user_id`/`anonymous_id` + any targeting attrs).
592
613
  */
593
614
  attributes?: AttributesFn<U>;
615
+ /**
616
+ * Long-running server: start the **background poll** internally (initial fetch
617
+ * + periodic refresh) so flags stay fresh without a redeploy. Default `false`.
618
+ * With `poll: false` (default) a one-shot fetch is kicked off fire-and-forget
619
+ * (serverless-friendly). You never need to call `engine.init()` yourself —
620
+ * configuration owns the lifecycle.
621
+ */
622
+ poll?: boolean;
623
+ /**
624
+ * One-shot fetch on configure (fire-and-forget). Default `true`. Ignored when
625
+ * `poll: true` (the poll does the initial fetch). Set `false` only if you want
626
+ * to control the first fetch yourself.
627
+ */
628
+ init?: boolean;
594
629
  }
595
630
  /**
596
631
  * Configure the SDK once at app boot, then evaluate per user with
@@ -613,6 +648,77 @@ interface ConfigureOptions<U = unknown> extends Omit<EngineOptions, "apiKey"> {
613
648
  declare function configure<U = unknown>(opts: ConfigureOptions<U>): Engine;
614
649
  /** Test seam: reset the registered attribute transform. */
615
650
  declare function _resetConfigureForTests(): void;
651
+ /** Seed shapes shared by {@link configureForTesting} / {@link configureForOffline}. */
652
+ interface ConfigureTestOptions<U = unknown> {
653
+ /** Same transform as {@link configure} (default identity). */
654
+ attributes?: AttributesFn<U>;
655
+ /** `{ name: bool }` — forced `getFlag` results. */
656
+ flags?: Record<string, boolean>;
657
+ /** `{ name: value }` — forced `getConfig` results. */
658
+ configs?: Record<string, unknown>;
659
+ /** `{ name: [group, params] }` — forced enrolments. */
660
+ experiments?: Record<string, [string, Record<string, unknown>]>;
661
+ }
662
+ /**
663
+ * Configure the SDK in **test mode** — a drop-in sibling of {@link configure}
664
+ * with no network, ever (no api key needed). Seed what your code under test
665
+ * should see, then read through the ordinary `new Client(user)`:
666
+ *
667
+ * ```ts
668
+ * configureForTesting({ flags: { new_checkout: true } });
669
+ * const flags = new Client({ user_id: "u_1" });
670
+ * flags.getFlag("new_checkout"); // true
671
+ * ```
672
+ *
673
+ * Replaces any previously-configured engine, so tests can reconfigure freely.
674
+ */
675
+ declare function configureForTesting<U = unknown>(opts?: ConfigureTestOptions<U>): Engine;
676
+ /** Options for {@link configureForOffline} — exactly one of `snapshot` / `path`. */
677
+ interface ConfigureOfflineOptions<U = unknown> extends ConfigureTestOptions<U> {
678
+ /** In-memory `{ flags: <body of /sdk/flags>, experiments: <body of /sdk/experiments> }`. */
679
+ snapshot?: {
680
+ flags: FlagsBlob;
681
+ experiments: ExpsBlob;
682
+ };
683
+ /** Path to a JSON file `{ flags, experiments }`. */
684
+ path?: string;
685
+ }
686
+ /**
687
+ * Configure the SDK **offline** — evaluate the REAL rules from an in-memory
688
+ * snapshot or a JSON file, with no network. A drop-in sibling of
689
+ * {@link configure} (no api key needed). Optional `flags`/`configs`/`experiments`
690
+ * overrides are layered on top (same shapes as {@link configureForTesting}).
691
+ * Replaces any previously-configured engine.
692
+ */
693
+ declare function configureForOffline<U = unknown>(opts: ConfigureOfflineOptions<U>): Engine;
694
+ /**
695
+ * Force `getFlag(name)` → `value` on the spot, for the current config. A quick
696
+ * in-test override layered on top of whatever {@link configureForTesting} /
697
+ * {@link configureForOffline} (or {@link configure}) set up — wins over the blob
698
+ * until {@link clearOverrides}.
699
+ */
700
+ declare function overrideFlag(name: string, value: boolean): void;
701
+ /** Force `getConfig(name)` → `value` on the spot (see {@link overrideFlag}). */
702
+ declare function overrideConfig(name: string, value: unknown): void;
703
+ /**
704
+ * Force `getExperiment(name)` to report enrolment in `group` with `params` on
705
+ * the spot (see {@link overrideFlag}).
706
+ */
707
+ declare function overrideExperiment(name: string, group: string, params: Record<string, unknown>): void;
708
+ /**
709
+ * Drop every on-the-spot flag/config/experiment override — INCLUDING the seed
710
+ * from {@link configureForTesting} (test mode has no blob beneath, so everything
711
+ * reverts to the empty-blob defaults). Under {@link configureForOffline} the
712
+ * snapshot remains and evaluations revert to it.
713
+ */
714
+ declare function clearOverrides(): void;
715
+ /**
716
+ * Register a listener fired after a background poll fetches NEW data (a 200, not
717
+ * a 304). Returns an unsubscribe callable. Requires `configure({ poll: true })`
718
+ * (no poll thread runs otherwise). Configuration owns the engine; you never
719
+ * touch it.
720
+ */
721
+ declare function onChange(listener: () => void): () => void;
616
722
  /**
617
723
  * A user-bound evaluation handle. Construct one per user/request — it's cheap
618
724
  * (it delegates to the {@link Engine} built by {@link configure}); it does NOT
@@ -637,6 +743,21 @@ declare class Client<U = unknown> {
637
743
  getExperiment<P extends Record<string, unknown>>(name: string, defaultParams: P, decode?: (raw: unknown) => P): ExperimentResult<P>;
638
744
  /** Read a killswitch (not user-bound; mirrors {@link Engine.getKillswitch}). */
639
745
  getKillswitch(name: string, switchKey?: string): boolean;
746
+ /**
747
+ * Record a conversion/metric event for the bound user. Derives the unit from
748
+ * the resolved attribute bag (`user_id`, else `anonymous_id`) and delegates to
749
+ * {@link Engine.track} — so an experiment is end-to-end Client-only (no need to
750
+ * drop down to the Engine to log a conversion). Fire-and-forget; no-op in test
751
+ * mode.
752
+ */
753
+ track(eventName: string, props?: Record<string, unknown>): void;
754
+ /**
755
+ * Emit an exposure event for `name` at this server-side decision point for the
756
+ * bound user. Delegates to {@link Engine.logExposure} with the resolved
757
+ * attribute bag (re-evaluates enrolment; no-op when the user isn't enrolled or
758
+ * in test mode).
759
+ */
760
+ logExposure(name: string): void;
640
761
  }
641
762
  interface SeeApi {
642
763
  /**
@@ -689,4 +810,4 @@ interface SeeApi {
689
810
  */
690
811
  declare const see: SeeApi;
691
812
 
692
- export { ANON_ID_COOKIE, type AttributesFn, type BootstrapData, type BootstrapEmitOptions, type BootstrapPayload, Client, type ConfigureOptions, type Consequence, Engine, type EngineEnv, type EngineOptions, type ExperimentResult, type ExpsBlob, FLAG_REASONS, type FetchLabelsOptions, type FlagDetail, type FlagReason, type FlagsBlob, type GetConfigOptions, type I18nForRequest, type LabelFile, type ScriptTagSpec, type SeeApi, type SeeChain, type SeeControlFlowChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyServerConfig, type ShipeasyServerHandle, type StickyBucketStore, type StickyEntry, type User, type Violation, _murmur3ForTests, _resetConfigureForTests, _resetShipeasyServerForTests, configure, configureShipeasyServer, createInMemoryStickyStore, fetchLabelsForSSR, flags, getBootstrapData, getBootstrapTags, getShipeasyServerClient, i18n, isExpected, see, seeContext, shipeasy, version };
813
+ export { ANON_ID_COOKIE, type AttributesFn, type BootstrapData, type BootstrapEmitOptions, type BootstrapPayload, Client, type ConfigureOfflineOptions, type ConfigureOptions, type ConfigureTestOptions, type Consequence, Engine, type EngineEnv, type EngineOptions, type ExperimentResult, type ExpsBlob, FLAG_REASONS, type FetchLabelsOptions, type FlagDetail, type FlagReason, type FlagsBlob, type GetConfigOptions, type I18nForRequest, LOG_LEVELS, type LabelFile, type LogLevel, type ScriptTagSpec, type SeeApi, type SeeChain, type SeeControlFlowChain, type SeeErrorEvent, type SeeExtras, type SeeKind, type SeeViolationChain, type ShipeasyServerConfig, type ShipeasyServerHandle, type StickyBucketStore, type StickyEntry, type User, type Violation, _murmur3ForTests, _resetConfigureForTests, _resetShipeasyServerForTests, clearOverrides, configure, configureForOffline, configureForTesting, configureShipeasyServer, createInMemoryStickyStore, fetchLabelsForSSR, flags, getBootstrapData, getBootstrapTags, getShipeasyServerClient, i18n, isExpected, onChange, overrideConfig, overrideExperiment, overrideFlag, see, seeContext, shipeasy, version };