@shipeasy/sdk 1.0.0 → 1.2.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/LICENSE ADDED
@@ -0,0 +1,40 @@
1
+ Shipeasy Source-Available License (Shipeasy-SAL) 1.0
2
+
3
+ Copyright (c) 2026 Shipeasy, Inc. All rights reserved.
4
+
5
+ 1. License Grant.
6
+ Subject to the terms of this License, Shipeasy, Inc. ("Shipeasy") grants
7
+ you a non-exclusive, non-transferable, revocable, worldwide license to:
8
+
9
+ (a) Use, copy, and modify the Software solely as a client integration for
10
+ interacting with Shipeasy's hosted services (the "Service");
11
+ (b) Distribute the Software as part of an application that calls the
12
+ Service, in object form, provided the recipient also agrees to this
13
+ License.
14
+
15
+ 2. Restrictions.
16
+ You may not:
17
+
18
+ (a) Use the Software, in whole or in part, to build, host, or operate any
19
+ service that competes with the Service or that provides feature-flag,
20
+ experimentation, configuration, internationalization, or related
21
+ functionality to third parties on a commercial basis;
22
+ (b) Sublicense, sell, rent, or lease the Software;
23
+ (c) Remove or alter copyright notices, license terms, or attribution.
24
+
25
+ 3. Contributions.
26
+ Any pull request you submit is licensed back to Shipeasy under this
27
+ License plus a perpetual, irrevocable right for Shipeasy to relicense.
28
+
29
+ 4. Trademarks.
30
+ This License does not grant rights in the names "Shipeasy", related
31
+ marks, or logos.
32
+
33
+ 5. No Warranty / Limitation of Liability.
34
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. IN NO
35
+ EVENT SHALL SHIPEASY BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
36
+ LIABILITY ARISING FROM USE OF THE SOFTWARE.
37
+
38
+ 6. Termination.
39
+ This License terminates automatically if you breach it. Sections 2-5
40
+ survive termination.
package/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # @shipeasy/sdk
2
+
3
+ Feature gates, runtime configs, experiments, and metrics for the
4
+ [Shipeasy](https://shipeasy.ai) hosted service.
5
+
6
+ > Source-available under the [Shipeasy-SAL 1.0](./LICENSE). Use it freely
7
+ > as a client of Shipeasy. Don't use it to build a competing service.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install @shipeasy/sdk
13
+ # or
14
+ pnpm add @shipeasy/sdk
15
+ ```
16
+
17
+ For React projects use [`@shipeasy/sdk-react`](https://github.com/shipeasy-ai/sdk-react)
18
+ which wraps this package with a `<ShipeasyProvider>` and hooks.
19
+
20
+ ## Quickstart — browser
21
+
22
+ ```ts
23
+ import { FlagsClientBrowser } from "@shipeasy/sdk/client";
24
+
25
+ const client = new FlagsClientBrowser({
26
+ sdkKey: process.env.NEXT_PUBLIC_SHIPEASY_CLIENT_KEY!,
27
+ });
28
+
29
+ await client.identify({ user_id: "user-123", plan: "pro" });
30
+
31
+ if (client.getFlag("new_checkout")) {
32
+ // ship it
33
+ }
34
+
35
+ const cfg = client.getConfig<{ max_uploads: number }>("upload_limits");
36
+ const { params } = client.getExperiment("hero_cta", {
37
+ primary_label: "Sign up",
38
+ });
39
+ ```
40
+
41
+ `identify()` automatically merges browser context (`locale`, `timezone`,
42
+ `path`, `referrer`, `screen_*`, `user_agent`) and a persisted
43
+ `anonymous_id` into the payload — so gate rules can target by locale or
44
+ holdouts can hash anonymous visitors out of the box.
45
+
46
+ ## Quickstart — server (Node, Cloudflare Worker, Deno)
47
+
48
+ ```ts
49
+ import { FlagsClient } from "@shipeasy/sdk/server";
50
+
51
+ const client = new FlagsClient({ sdkKey: process.env.SHIPEASY_SERVER_KEY! });
52
+
53
+ const cfg = await client.getConfig("plan_limits", { user_id: "u-1" });
54
+ ```
55
+
56
+ ## Drop-in `<script>` loader (no bundler)
57
+
58
+ ```html
59
+ <script
60
+ src="https://cdn.shipeasy.ai/sdk/loader.js"
61
+ data-sdk-key="sdk_client_..."
62
+ data-user-id="user-123"
63
+ data-user-email="u@x.com"
64
+ data-user-plan="pro"
65
+ data-attrs='{"country":"US"}'
66
+ defer
67
+ ></script>
68
+ <script>
69
+ await window.shipeasy.ready;
70
+ if (window.shipeasy.getFlag("new_checkout")) { /* … */ }
71
+ </script>
72
+ ```
73
+
74
+ The loader IIFE is published to a public R2 bucket on every release and
75
+ cached for 1y at `loader-vX.Y.Z.js` (immutable) plus a rolling 5-minute
76
+ `loader.js`.
77
+
78
+ ## Devtools overlay
79
+
80
+ Press `Shift+Alt+S` on any page running the SDK (or append `?se=1` to the
81
+ URL). The Shipeasy devtools panel mounts in a Shadow DOM overlay and lets
82
+ you flip every gate / config / experiment / translation **for the current
83
+ session only** — handy for QA, demos, and bug repro.
84
+
85
+ ## Documentation
86
+
87
+ Full docs at [docs.shipeasy.ai](https://docs.shipeasy.ai). API surfaces
88
+ covered there: targeting rules, holdouts, sequential stats, custom
89
+ metrics, Slack digests, OAuth/SSO, Claude/MCP integration.
90
+
91
+ ## License
92
+
93
+ [Shipeasy-SAL 1.0](./LICENSE) — source-available, non-commercial-use,
94
+ permitted for use as a Shipeasy client.
@@ -1,3 +1,13 @@
1
+ declare global {
2
+ interface Window {
3
+ i18n?: {
4
+ t: (key: string, vars?: Record<string, string | number>) => string;
5
+ ready: (cb: () => void) => void;
6
+ on: (event: "update", cb: () => void) => () => void;
7
+ locale: string | null;
8
+ };
9
+ }
10
+ }
1
11
  declare const version = "1.0.0";
2
12
  interface User {
3
13
  user_id?: string;
@@ -8,41 +18,167 @@ interface ExperimentResult<P> {
8
18
  group: string;
9
19
  params: P;
10
20
  }
11
- interface EvalFlagResult {
12
- enabled: boolean;
13
- }
14
21
  interface EvalExpResult {
15
- in_experiment: boolean;
22
+ inExperiment: boolean;
16
23
  group: string;
17
24
  params: Record<string, unknown>;
18
25
  }
19
26
  interface EvalResponse {
20
- flags: Record<string, EvalFlagResult>;
27
+ flags: Record<string, boolean>;
28
+ configs: Record<string, unknown>;
21
29
  experiments: Record<string, EvalExpResult>;
22
30
  }
31
+ type FlagsClientBrowserEnv = "dev" | "staging" | "prod";
23
32
  interface FlagsClientBrowserOptions {
24
33
  sdkKey: string;
25
34
  baseUrl?: string;
26
35
  autoGuardrails?: boolean;
36
+ /** Which published env to read values from. Defaults to "prod". */
37
+ env?: FlagsClientBrowserEnv;
27
38
  }
28
39
  declare class FlagsClientBrowser {
29
40
  private readonly sdkKey;
30
41
  private readonly baseUrl;
31
42
  private readonly autoGuardrails;
43
+ private readonly env;
32
44
  private evalResult;
33
45
  private anonId;
34
46
  private userId;
35
47
  private buffer;
36
48
  private guardrailsInstalled;
49
+ private listeners;
50
+ private overrideListenerInstalled;
51
+ private onOverrideChange;
37
52
  constructor(opts: FlagsClientBrowserOptions);
38
53
  identify(user: User): Promise<void>;
54
+ get ready(): boolean;
55
+ private notify;
39
56
  initFromBootstrap(data: EvalResponse): void;
40
57
  getFlag(name: string): boolean;
41
58
  getConfig<T = unknown>(name: string, decode?: (raw: unknown) => T): T | undefined;
42
- getExperiment<P extends Record<string, unknown>>(name: string, defaultParams: P, decode?: (raw: unknown) => P): ExperimentResult<P>;
59
+ getExperiment<P extends Record<string, unknown>>(name: string, defaultParams: P, decode?: (raw: unknown) => P, variants?: Record<string, Partial<P>>): ExperimentResult<P>;
60
+ /**
61
+ * Subscribe to state changes — fires after identify() completes and on
62
+ * `se:override:change` events from the devtools overlay. Returns an
63
+ * unsubscribe function. Used by framework adapters to trigger re-renders.
64
+ */
65
+ subscribe(listener: () => void): () => void;
66
+ /**
67
+ * Publishes the SDK to `window.__shipeasy` so the devtools overlay can read
68
+ * current values. Idempotent. Returns the bridge object for tests.
69
+ */
70
+ installBridge(): ShipeasySdkBridge | null;
43
71
  track(eventName: string, props?: Record<string, unknown>): void;
44
72
  flush(): Promise<void>;
45
73
  destroy(): void;
46
74
  }
75
+ declare function readGateOverride(name: string): boolean | null;
76
+ declare function readConfigOverride(name: string): unknown;
77
+ declare function readExpOverride(name: string): string | null;
78
+ declare function isDevtoolsRequested(): boolean;
79
+ /** Bridge written to window.__shipeasy — mirrors @shipeasy/devtools' contract. */
80
+ interface ShipeasySdkBridge {
81
+ getFlag(name: string): boolean;
82
+ getExperiment(name: string): {
83
+ inExperiment: boolean;
84
+ group: string;
85
+ } | undefined;
86
+ getConfig(name: string): unknown;
87
+ }
88
+ /**
89
+ * If the host page already mounted the standalone devtools IIFE bundle (which
90
+ * exposes `window.__shipeasy_devtools_global`), call its init() and wire up a
91
+ * toggle handle at `window.__shipeasy_devtools`. No-op when the bundle is
92
+ * absent — the customer is responsible for mounting it themselves.
93
+ */
94
+ declare function loadDevtools(opts?: {
95
+ adminUrl?: string;
96
+ edgeUrl?: string;
97
+ }): void;
98
+ interface AttachDevtoolsOptions {
99
+ /** Hotkey string in the form "Shift+Alt+S". */
100
+ hotkey?: string;
101
+ adminUrl?: string;
102
+ edgeUrl?: string;
103
+ }
104
+ /**
105
+ * One-call bootstrap for the devtools overlay. Installs the bridge, optionally
106
+ * auto-loads the overlay if the page was opened with `?se`, registers a hotkey
107
+ * listener for opening/toggling the overlay, and re-publishes the bridge after
108
+ * each `identify()`/override change. Returns an unsubscribe function for
109
+ * cleanup (e.g. React effect teardown).
110
+ */
111
+ declare function attachDevtools(client: FlagsClientBrowser, opts?: AttachDevtoolsOptions): () => void;
112
+ /** Configure the singleton. Idempotent — re-calling with the same opts is a no-op. */
113
+ declare function configureShipeasy(opts: FlagsClientBrowserOptions): FlagsClientBrowser;
114
+ /** Returns the configured singleton, or null if configureShipeasy() hasn't run yet. */
115
+ declare function getShipeasyClient(): FlagsClientBrowser | null;
116
+ /**
117
+ * Test helper — drop the singleton so the next configureShipeasy() builds fresh.
118
+ * Not part of the documented surface; production code should never call this.
119
+ */
120
+ declare function _resetShipeasyForTests(): void;
121
+ /**
122
+ * Universal flags facade. Methods return safe defaults when the singleton
123
+ * hasn't been configured yet (false / undefined / `notIn` experiment), so
124
+ * importing this in a module that loads before app boot is harmless.
125
+ */
126
+ declare const flags: {
127
+ configure(opts: FlagsClientBrowserOptions): void;
128
+ identify(user: User): Promise<void>;
129
+ /** Read a feature gate. Returns false until identify() resolves. */
130
+ get(name: string): boolean;
131
+ getConfig<T = unknown>(name: string, decode?: (raw: unknown) => T): T | undefined;
132
+ getExperiment<P extends Record<string, unknown>>(name: string, defaultParams: P, decode?: (raw: unknown) => P, variants?: Record<string, Partial<P>>): ExperimentResult<P>;
133
+ track(eventName: string, props?: Record<string, unknown>): void;
134
+ flush(): Promise<void>;
135
+ /** Subscribe for change notifications (identify/override). Used by framework adapters. */
136
+ subscribe(listener: () => void): () => void;
137
+ /** True once identify() has completed and flags are available. */
138
+ readonly ready: boolean;
139
+ };
140
+ declare const LABEL_MARKER_START = "\uFFF9";
141
+ declare const LABEL_MARKER_SEP = "\uFFFA";
142
+ declare const LABEL_MARKER_END = "\uFFFB";
143
+ declare const LABEL_MARKER_RE: RegExp;
144
+ declare function encodeLabelMarker(key: string, value: string): string;
145
+ interface LabelAttrs {
146
+ "data-label": string;
147
+ "data-variables"?: string;
148
+ "data-label-desc"?: string;
149
+ }
150
+ declare function labelAttrs(key: string, variables?: Record<string, string | number>, desc?: string): LabelAttrs;
151
+ /**
152
+ * Universal i18n facade. Backed by the `window.i18n` global the loader
153
+ * script installs. Returns the key itself when the loader hasn't run
154
+ * (SSR, missing script tag, before profile fetch completes), so call
155
+ * sites never need to null-check.
156
+ */
157
+ declare const i18n: {
158
+ t(key: string, variables?: Record<string, string | number>): string;
159
+ /**
160
+ * Translate a key and return a framework element (e.g. React <span>)
161
+ * carrying `data-label` / `data-variables` attributes so the ShipEasy
162
+ * devtools "Edit labels" overlay can highlight and edit it in place.
163
+ *
164
+ * Requires a one-time setup call: `i18n.configure({ createElement })`.
165
+ * The returned value is whatever `createElement` returns — pass React's
166
+ * `createElement`, Vue's `h`, Solid's `createSignal`-based factory, etc.
167
+ *
168
+ * Falls back to a plain translated string if `createElement` was not
169
+ * configured (e.g. server-side or in non-JSX contexts).
170
+ */
171
+ tEl(key: string, variables?: Record<string, string | number>, desc?: string): any;
172
+ /** Wire up the element creator once at app startup (call before any tEl use). */
173
+ configure(opts: {
174
+ createElement: (tag: string, props: object, children: string) => any;
175
+ }): void;
176
+ readonly locale: string | null;
177
+ readonly ready: boolean;
178
+ /** Resolves when the loader has installed window.i18n and fetched a profile. */
179
+ whenReady(): Promise<void>;
180
+ /** Subscribe to locale/profile updates. Returns an unsubscribe fn. */
181
+ onUpdate(cb: () => void): () => void;
182
+ };
47
183
 
48
- export { type ExperimentResult, FlagsClientBrowser, type FlagsClientBrowserOptions, type User, version };
184
+ export { type ExperimentResult, FlagsClientBrowser, type FlagsClientBrowserEnv, type FlagsClientBrowserOptions, LABEL_MARKER_END, LABEL_MARKER_RE, LABEL_MARKER_SEP, LABEL_MARKER_START, type LabelAttrs, type ShipeasySdkBridge, type User, _resetShipeasyForTests, attachDevtools, configureShipeasy, encodeLabelMarker, flags, getShipeasyClient, i18n, isDevtoolsRequested, labelAttrs, loadDevtools, readConfigOverride, readExpOverride, readGateOverride, version };
@@ -1,3 +1,13 @@
1
+ declare global {
2
+ interface Window {
3
+ i18n?: {
4
+ t: (key: string, vars?: Record<string, string | number>) => string;
5
+ ready: (cb: () => void) => void;
6
+ on: (event: "update", cb: () => void) => () => void;
7
+ locale: string | null;
8
+ };
9
+ }
10
+ }
1
11
  declare const version = "1.0.0";
2
12
  interface User {
3
13
  user_id?: string;
@@ -8,41 +18,167 @@ interface ExperimentResult<P> {
8
18
  group: string;
9
19
  params: P;
10
20
  }
11
- interface EvalFlagResult {
12
- enabled: boolean;
13
- }
14
21
  interface EvalExpResult {
15
- in_experiment: boolean;
22
+ inExperiment: boolean;
16
23
  group: string;
17
24
  params: Record<string, unknown>;
18
25
  }
19
26
  interface EvalResponse {
20
- flags: Record<string, EvalFlagResult>;
27
+ flags: Record<string, boolean>;
28
+ configs: Record<string, unknown>;
21
29
  experiments: Record<string, EvalExpResult>;
22
30
  }
31
+ type FlagsClientBrowserEnv = "dev" | "staging" | "prod";
23
32
  interface FlagsClientBrowserOptions {
24
33
  sdkKey: string;
25
34
  baseUrl?: string;
26
35
  autoGuardrails?: boolean;
36
+ /** Which published env to read values from. Defaults to "prod". */
37
+ env?: FlagsClientBrowserEnv;
27
38
  }
28
39
  declare class FlagsClientBrowser {
29
40
  private readonly sdkKey;
30
41
  private readonly baseUrl;
31
42
  private readonly autoGuardrails;
43
+ private readonly env;
32
44
  private evalResult;
33
45
  private anonId;
34
46
  private userId;
35
47
  private buffer;
36
48
  private guardrailsInstalled;
49
+ private listeners;
50
+ private overrideListenerInstalled;
51
+ private onOverrideChange;
37
52
  constructor(opts: FlagsClientBrowserOptions);
38
53
  identify(user: User): Promise<void>;
54
+ get ready(): boolean;
55
+ private notify;
39
56
  initFromBootstrap(data: EvalResponse): void;
40
57
  getFlag(name: string): boolean;
41
58
  getConfig<T = unknown>(name: string, decode?: (raw: unknown) => T): T | undefined;
42
- getExperiment<P extends Record<string, unknown>>(name: string, defaultParams: P, decode?: (raw: unknown) => P): ExperimentResult<P>;
59
+ getExperiment<P extends Record<string, unknown>>(name: string, defaultParams: P, decode?: (raw: unknown) => P, variants?: Record<string, Partial<P>>): ExperimentResult<P>;
60
+ /**
61
+ * Subscribe to state changes — fires after identify() completes and on
62
+ * `se:override:change` events from the devtools overlay. Returns an
63
+ * unsubscribe function. Used by framework adapters to trigger re-renders.
64
+ */
65
+ subscribe(listener: () => void): () => void;
66
+ /**
67
+ * Publishes the SDK to `window.__shipeasy` so the devtools overlay can read
68
+ * current values. Idempotent. Returns the bridge object for tests.
69
+ */
70
+ installBridge(): ShipeasySdkBridge | null;
43
71
  track(eventName: string, props?: Record<string, unknown>): void;
44
72
  flush(): Promise<void>;
45
73
  destroy(): void;
46
74
  }
75
+ declare function readGateOverride(name: string): boolean | null;
76
+ declare function readConfigOverride(name: string): unknown;
77
+ declare function readExpOverride(name: string): string | null;
78
+ declare function isDevtoolsRequested(): boolean;
79
+ /** Bridge written to window.__shipeasy — mirrors @shipeasy/devtools' contract. */
80
+ interface ShipeasySdkBridge {
81
+ getFlag(name: string): boolean;
82
+ getExperiment(name: string): {
83
+ inExperiment: boolean;
84
+ group: string;
85
+ } | undefined;
86
+ getConfig(name: string): unknown;
87
+ }
88
+ /**
89
+ * If the host page already mounted the standalone devtools IIFE bundle (which
90
+ * exposes `window.__shipeasy_devtools_global`), call its init() and wire up a
91
+ * toggle handle at `window.__shipeasy_devtools`. No-op when the bundle is
92
+ * absent — the customer is responsible for mounting it themselves.
93
+ */
94
+ declare function loadDevtools(opts?: {
95
+ adminUrl?: string;
96
+ edgeUrl?: string;
97
+ }): void;
98
+ interface AttachDevtoolsOptions {
99
+ /** Hotkey string in the form "Shift+Alt+S". */
100
+ hotkey?: string;
101
+ adminUrl?: string;
102
+ edgeUrl?: string;
103
+ }
104
+ /**
105
+ * One-call bootstrap for the devtools overlay. Installs the bridge, optionally
106
+ * auto-loads the overlay if the page was opened with `?se`, registers a hotkey
107
+ * listener for opening/toggling the overlay, and re-publishes the bridge after
108
+ * each `identify()`/override change. Returns an unsubscribe function for
109
+ * cleanup (e.g. React effect teardown).
110
+ */
111
+ declare function attachDevtools(client: FlagsClientBrowser, opts?: AttachDevtoolsOptions): () => void;
112
+ /** Configure the singleton. Idempotent — re-calling with the same opts is a no-op. */
113
+ declare function configureShipeasy(opts: FlagsClientBrowserOptions): FlagsClientBrowser;
114
+ /** Returns the configured singleton, or null if configureShipeasy() hasn't run yet. */
115
+ declare function getShipeasyClient(): FlagsClientBrowser | null;
116
+ /**
117
+ * Test helper — drop the singleton so the next configureShipeasy() builds fresh.
118
+ * Not part of the documented surface; production code should never call this.
119
+ */
120
+ declare function _resetShipeasyForTests(): void;
121
+ /**
122
+ * Universal flags facade. Methods return safe defaults when the singleton
123
+ * hasn't been configured yet (false / undefined / `notIn` experiment), so
124
+ * importing this in a module that loads before app boot is harmless.
125
+ */
126
+ declare const flags: {
127
+ configure(opts: FlagsClientBrowserOptions): void;
128
+ identify(user: User): Promise<void>;
129
+ /** Read a feature gate. Returns false until identify() resolves. */
130
+ get(name: string): boolean;
131
+ getConfig<T = unknown>(name: string, decode?: (raw: unknown) => T): T | undefined;
132
+ getExperiment<P extends Record<string, unknown>>(name: string, defaultParams: P, decode?: (raw: unknown) => P, variants?: Record<string, Partial<P>>): ExperimentResult<P>;
133
+ track(eventName: string, props?: Record<string, unknown>): void;
134
+ flush(): Promise<void>;
135
+ /** Subscribe for change notifications (identify/override). Used by framework adapters. */
136
+ subscribe(listener: () => void): () => void;
137
+ /** True once identify() has completed and flags are available. */
138
+ readonly ready: boolean;
139
+ };
140
+ declare const LABEL_MARKER_START = "\uFFF9";
141
+ declare const LABEL_MARKER_SEP = "\uFFFA";
142
+ declare const LABEL_MARKER_END = "\uFFFB";
143
+ declare const LABEL_MARKER_RE: RegExp;
144
+ declare function encodeLabelMarker(key: string, value: string): string;
145
+ interface LabelAttrs {
146
+ "data-label": string;
147
+ "data-variables"?: string;
148
+ "data-label-desc"?: string;
149
+ }
150
+ declare function labelAttrs(key: string, variables?: Record<string, string | number>, desc?: string): LabelAttrs;
151
+ /**
152
+ * Universal i18n facade. Backed by the `window.i18n` global the loader
153
+ * script installs. Returns the key itself when the loader hasn't run
154
+ * (SSR, missing script tag, before profile fetch completes), so call
155
+ * sites never need to null-check.
156
+ */
157
+ declare const i18n: {
158
+ t(key: string, variables?: Record<string, string | number>): string;
159
+ /**
160
+ * Translate a key and return a framework element (e.g. React <span>)
161
+ * carrying `data-label` / `data-variables` attributes so the ShipEasy
162
+ * devtools "Edit labels" overlay can highlight and edit it in place.
163
+ *
164
+ * Requires a one-time setup call: `i18n.configure({ createElement })`.
165
+ * The returned value is whatever `createElement` returns — pass React's
166
+ * `createElement`, Vue's `h`, Solid's `createSignal`-based factory, etc.
167
+ *
168
+ * Falls back to a plain translated string if `createElement` was not
169
+ * configured (e.g. server-side or in non-JSX contexts).
170
+ */
171
+ tEl(key: string, variables?: Record<string, string | number>, desc?: string): any;
172
+ /** Wire up the element creator once at app startup (call before any tEl use). */
173
+ configure(opts: {
174
+ createElement: (tag: string, props: object, children: string) => any;
175
+ }): void;
176
+ readonly locale: string | null;
177
+ readonly ready: boolean;
178
+ /** Resolves when the loader has installed window.i18n and fetched a profile. */
179
+ whenReady(): Promise<void>;
180
+ /** Subscribe to locale/profile updates. Returns an unsubscribe fn. */
181
+ onUpdate(cb: () => void): () => void;
182
+ };
47
183
 
48
- export { type ExperimentResult, FlagsClientBrowser, type FlagsClientBrowserOptions, type User, version };
184
+ export { type ExperimentResult, FlagsClientBrowser, type FlagsClientBrowserEnv, type FlagsClientBrowserOptions, LABEL_MARKER_END, LABEL_MARKER_RE, LABEL_MARKER_SEP, LABEL_MARKER_START, type LabelAttrs, type ShipeasySdkBridge, type User, _resetShipeasyForTests, attachDevtools, configureShipeasy, encodeLabelMarker, flags, getShipeasyClient, i18n, isDevtoolsRequested, labelAttrs, loadDevtools, readConfigOverride, readExpOverride, readGateOverride, version };