@shipeasy/sdk 2.0.3 → 2.1.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.
@@ -110,6 +110,20 @@ interface AttachDevtoolsOptions {
110
110
  */
111
111
  declare function attachDevtools(client: FlagsClientBrowser, opts?: AttachDevtoolsOptions): () => void;
112
112
  /** Configure the singleton. Idempotent — re-calling with the same opts is a no-op. */
113
+ interface ShipeasyClientConfig {
114
+ /** SDK key — same value used on the server via shipeasy(). */
115
+ apiKey: string;
116
+ /** Override the ShipEasy CDN/edge base URL. Defaults to https://cdn.shipeasy.ai. */
117
+ baseUrl?: string;
118
+ /** Override the admin URL for the devtools overlay (dev use). */
119
+ adminUrl?: string;
120
+ }
121
+ /**
122
+ * Initialise the ShipEasy client SDK and wire up lazy devtools.
123
+ * Call this once at app startup (e.g. in a useEffect in your root layout).
124
+ * Returns a cleanup function — call it on unmount to remove event listeners.
125
+ */
126
+ declare function shipeasy(opts: ShipeasyClientConfig): () => void;
113
127
  declare function configureShipeasy(opts: FlagsClientBrowserOptions): FlagsClientBrowser;
114
128
  /** Returns the configured singleton, or null if configureShipeasy() hasn't run yet. */
115
129
  declare function getShipeasyClient(): FlagsClientBrowser | null;
@@ -126,6 +140,9 @@ interface BootstrapPayload {
126
140
  group: string;
127
141
  params: Record<string, unknown>;
128
142
  }>;
143
+ /** Set by getBootstrapHtml() for auto-init. Not part of evaluate() output. */
144
+ apiKey?: string;
145
+ apiUrl?: string;
129
146
  }
130
147
  /**
131
148
  * Universal flags facade. Methods return safe defaults when the singleton
@@ -206,4 +223,4 @@ declare const i18n: {
206
223
  onUpdate(cb: () => void): () => void;
207
224
  };
208
225
 
209
- export { type BootstrapPayload, 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 };
226
+ export { type BootstrapPayload, type ExperimentResult, FlagsClientBrowser, type FlagsClientBrowserEnv, type FlagsClientBrowserOptions, 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 };
@@ -110,6 +110,20 @@ interface AttachDevtoolsOptions {
110
110
  */
111
111
  declare function attachDevtools(client: FlagsClientBrowser, opts?: AttachDevtoolsOptions): () => void;
112
112
  /** Configure the singleton. Idempotent — re-calling with the same opts is a no-op. */
113
+ interface ShipeasyClientConfig {
114
+ /** SDK key — same value used on the server via shipeasy(). */
115
+ apiKey: string;
116
+ /** Override the ShipEasy CDN/edge base URL. Defaults to https://cdn.shipeasy.ai. */
117
+ baseUrl?: string;
118
+ /** Override the admin URL for the devtools overlay (dev use). */
119
+ adminUrl?: string;
120
+ }
121
+ /**
122
+ * Initialise the ShipEasy client SDK and wire up lazy devtools.
123
+ * Call this once at app startup (e.g. in a useEffect in your root layout).
124
+ * Returns a cleanup function — call it on unmount to remove event listeners.
125
+ */
126
+ declare function shipeasy(opts: ShipeasyClientConfig): () => void;
113
127
  declare function configureShipeasy(opts: FlagsClientBrowserOptions): FlagsClientBrowser;
114
128
  /** Returns the configured singleton, or null if configureShipeasy() hasn't run yet. */
115
129
  declare function getShipeasyClient(): FlagsClientBrowser | null;
@@ -126,6 +140,9 @@ interface BootstrapPayload {
126
140
  group: string;
127
141
  params: Record<string, unknown>;
128
142
  }>;
143
+ /** Set by getBootstrapHtml() for auto-init. Not part of evaluate() output. */
144
+ apiKey?: string;
145
+ apiUrl?: string;
129
146
  }
130
147
  /**
131
148
  * Universal flags facade. Methods return safe defaults when the singleton
@@ -206,4 +223,4 @@ declare const i18n: {
206
223
  onUpdate(cb: () => void): () => void;
207
224
  };
208
225
 
209
- export { type BootstrapPayload, 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 };
226
+ export { type BootstrapPayload, type ExperimentResult, FlagsClientBrowser, type FlagsClientBrowserEnv, type FlagsClientBrowserOptions, 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 };
@@ -38,6 +38,7 @@ __export(client_exports, {
38
38
  readConfigOverride: () => readConfigOverride,
39
39
  readExpOverride: () => readExpOverride,
40
40
  readGateOverride: () => readGateOverride,
41
+ shipeasy: () => shipeasy,
41
42
  version: () => version
42
43
  });
43
44
  module.exports = __toCommonJS(client_exports);
@@ -647,6 +648,14 @@ function attachDevtools(client, opts = {}) {
647
648
  };
648
649
  }
649
650
  var _client = null;
651
+ function shipeasy(opts) {
652
+ const client = configureShipeasy({
653
+ sdkKey: opts.apiKey,
654
+ baseUrl: opts.baseUrl ?? "https://cdn.shipeasy.ai"
655
+ });
656
+ flags.notifyMounted();
657
+ return attachDevtools(client, { adminUrl: opts.adminUrl });
658
+ }
650
659
  function configureShipeasy(opts) {
651
660
  if (_client) return _client;
652
661
  _client = new FlagsClientBrowser(opts);
@@ -774,9 +783,19 @@ function labelAttrs(key, variables, desc) {
774
783
  return attrs;
775
784
  }
776
785
  var _createElement = null;
786
+ var _I18N_SSR_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:ssr-i18n");
787
+ function getSSRI18nStore() {
788
+ return globalThis[_I18N_SSR_SYM]?.() ?? null;
789
+ }
790
+ function interpolate(raw, variables) {
791
+ if (!variables) return raw;
792
+ return raw.replace(/\{\{(\w+)\}\}/g, (_, k) => String(variables[k] ?? `{{${k}}}`));
793
+ }
777
794
  var i18n = {
778
795
  t(key, variables) {
779
796
  if (typeof window !== "undefined" && window.i18n) return window.i18n.t(key, variables);
797
+ const store = getSSRI18nStore();
798
+ if (store?.strings[key]) return interpolate(store.strings[key], variables);
780
799
  return key;
781
800
  },
782
801
  /**
@@ -792,7 +811,8 @@ var i18n = {
792
811
  * configured (e.g. server-side or in non-JSX contexts).
793
812
  */
794
813
  tEl(key, fallback, variables, desc) {
795
- const text = this.t(key, variables) || fallback;
814
+ const hasTranslation = typeof window !== "undefined" && Boolean(window.i18n) || Boolean(getSSRI18nStore()?.strings[key]);
815
+ const text = hasTranslation ? this.t(key, variables) || fallback : fallback;
796
816
  if (!_createElement) return text;
797
817
  return _createElement("span", labelAttrs(key, variables, desc), text);
798
818
  },
@@ -835,6 +855,12 @@ var i18n = {
835
855
  };
836
856
  }
837
857
  };
858
+ if (typeof window !== "undefined") {
859
+ const _initBs = window.__SE_BOOTSTRAP;
860
+ if (_initBs?.apiKey && !_client) {
861
+ shipeasy({ apiKey: _initBs.apiKey, baseUrl: _initBs.apiUrl });
862
+ }
863
+ }
838
864
  // Annotate the CommonJS export names for ESM import in node:
839
865
  0 && (module.exports = {
840
866
  FlagsClientBrowser,
@@ -855,5 +881,6 @@ var i18n = {
855
881
  readConfigOverride,
856
882
  readExpOverride,
857
883
  readGateOverride,
884
+ shipeasy,
858
885
  version
859
886
  });
@@ -605,6 +605,14 @@ function attachDevtools(client, opts = {}) {
605
605
  };
606
606
  }
607
607
  var _client = null;
608
+ function shipeasy(opts) {
609
+ const client = configureShipeasy({
610
+ sdkKey: opts.apiKey,
611
+ baseUrl: opts.baseUrl ?? "https://cdn.shipeasy.ai"
612
+ });
613
+ flags.notifyMounted();
614
+ return attachDevtools(client, { adminUrl: opts.adminUrl });
615
+ }
608
616
  function configureShipeasy(opts) {
609
617
  if (_client) return _client;
610
618
  _client = new FlagsClientBrowser(opts);
@@ -732,9 +740,19 @@ function labelAttrs(key, variables, desc) {
732
740
  return attrs;
733
741
  }
734
742
  var _createElement = null;
743
+ var _I18N_SSR_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:ssr-i18n");
744
+ function getSSRI18nStore() {
745
+ return globalThis[_I18N_SSR_SYM]?.() ?? null;
746
+ }
747
+ function interpolate(raw, variables) {
748
+ if (!variables) return raw;
749
+ return raw.replace(/\{\{(\w+)\}\}/g, (_, k) => String(variables[k] ?? `{{${k}}}`));
750
+ }
735
751
  var i18n = {
736
752
  t(key, variables) {
737
753
  if (typeof window !== "undefined" && window.i18n) return window.i18n.t(key, variables);
754
+ const store = getSSRI18nStore();
755
+ if (store?.strings[key]) return interpolate(store.strings[key], variables);
738
756
  return key;
739
757
  },
740
758
  /**
@@ -750,7 +768,8 @@ var i18n = {
750
768
  * configured (e.g. server-side or in non-JSX contexts).
751
769
  */
752
770
  tEl(key, fallback, variables, desc) {
753
- const text = this.t(key, variables) || fallback;
771
+ const hasTranslation = typeof window !== "undefined" && Boolean(window.i18n) || Boolean(getSSRI18nStore()?.strings[key]);
772
+ const text = hasTranslation ? this.t(key, variables) || fallback : fallback;
754
773
  if (!_createElement) return text;
755
774
  return _createElement("span", labelAttrs(key, variables, desc), text);
756
775
  },
@@ -793,6 +812,12 @@ var i18n = {
793
812
  };
794
813
  }
795
814
  };
815
+ if (typeof window !== "undefined") {
816
+ const _initBs = window.__SE_BOOTSTRAP;
817
+ if (_initBs?.apiKey && !_client) {
818
+ shipeasy({ apiKey: _initBs.apiKey, baseUrl: _initBs.apiUrl });
819
+ }
820
+ }
796
821
  export {
797
822
  FlagsClientBrowser,
798
823
  LABEL_MARKER_END,
@@ -812,5 +837,6 @@ export {
812
837
  readConfigOverride,
813
838
  readExpOverride,
814
839
  readGateOverride,
840
+ shipeasy,
815
841
  version
816
842
  };
@@ -55,6 +55,32 @@ declare class FlagsClient {
55
55
  */
56
56
  evaluate(user: User, rawUrl?: string): BootstrapPayload;
57
57
  }
58
+ interface I18nForRequest {
59
+ strings: Record<string, string>;
60
+ locale: string;
61
+ }
62
+ declare const i18n: {
63
+ /**
64
+ * Fetch translation labels for the current request and store them in an
65
+ * async-local context so `i18n.t()` / `i18n.tEl()` in SSR'd client
66
+ * components return the real translated strings instead of the key.
67
+ *
68
+ * Call once per request in the root layout (or page). Failure is silent —
69
+ * `i18n.t()` falls back to the hardcoded fallback arg when no labels are
70
+ * loaded.
71
+ *
72
+ * @param key SDK client key (NEXT_PUBLIC_SHIPEASY_CLIENT_KEY)
73
+ * @param profile i18n profile identifier, e.g. "en:prod"
74
+ * @param cdnBaseUrl Optional override for the i18n CDN (default: cdn.i18n.shipeasy.ai)
75
+ */
76
+ init(key: string, profile: string, cdnBaseUrl?: string): Promise<void>;
77
+ /**
78
+ * Return the translation strings loaded for the current request.
79
+ * Use this to include i18n data in the SSR bootstrap payload so the
80
+ * client doesn't need an extra network round-trip.
81
+ */
82
+ getForRequest(): I18nForRequest;
83
+ };
58
84
  interface LabelFile {
59
85
  v: number;
60
86
  profile: string;
@@ -72,6 +98,57 @@ declare function fetchLabelsForSSR(opts: FetchLabelsOptions): Promise<LabelFile
72
98
  declare function configureShipeasyServer(opts: FlagsClientOptions): FlagsClient;
73
99
  declare function getShipeasyServerClient(): FlagsClient | null;
74
100
  declare function _resetShipeasyServerForTests(): void;
101
+ interface ShipeasyServerConfig {
102
+ /**
103
+ * Server-side API key — authenticates flag/experiment fetches from the edge.
104
+ * Never embedded in browser output. A warning is logged if omitted.
105
+ */
106
+ apiKey?: string;
107
+ /**
108
+ * Public client key — embedded in window.__SE_BOOTSTRAP and used by the
109
+ * browser SDK. Safe to expose (e.g. NEXT_PUBLIC_ env vars).
110
+ * Defaults to apiKey for single-key setups.
111
+ */
112
+ clientKey?: string;
113
+ /** Raw URL or query string for applying ?se_ks_* / ?se_cf_* / ?se_exp_* overrides. */
114
+ urlOverrides?: string;
115
+ /** User attributes for flag and experiment evaluation. */
116
+ user?: User;
117
+ /** i18n profile to load for SSR translations, e.g. "en:prod". Defaults to "en:prod". */
118
+ i18nDefaultProfile?: string;
119
+ }
120
+ interface ShipeasyServerHandle {
121
+ flags: Record<string, boolean>;
122
+ configs: Record<string, unknown>;
123
+ experiments: Record<string, ExperimentResult<Record<string, unknown>>>;
124
+ /** Returns a vanilla-JS string for a single inline <script> tag. */
125
+ getBootstrapHtml(): string;
126
+ }
127
+ /**
128
+ * Initialise the ShipEasy server SDK, evaluate flags for this request, and
129
+ * return a handle. Call once per request in your root layout (or page for
130
+ * URL-override support). Failure is non-fatal — evaluation returns empty
131
+ * payloads and i18n falls back to hardcoded strings.
132
+ */
133
+ declare function shipeasy(opts: ShipeasyServerConfig): Promise<ShipeasyServerHandle>;
134
+ interface BootstrapHtmlOptions {
135
+ /** SDK client key */
136
+ apiKey: string;
137
+ /** i18n profile fed to the loader script. Defaults to "en:prod". */
138
+ i18nProfile?: string;
139
+ }
140
+ /**
141
+ * Returns a vanilla-JS script string for a single <script> tag.
142
+ * Handles everything the client needs at startup:
143
+ * - window.__se_devtools_config (when devtoolsAdminUrl is set)
144
+ * - window.__SE_BOOTSTRAP (flags + configs + experiments + i18n + apiKey for auto-init)
145
+ * - window.i18n shim from SSR strings (prevents hydration mismatches)
146
+ * - dynamic <script> injection for the i18n loader
147
+ *
148
+ * Framework-agnostic: set innerHTML on a <script> element, nothing else required.
149
+ * Pass null for bootstrap on pages without flag evaluation — client still auto-inits.
150
+ */
151
+ declare function getBootstrapHtml(bootstrap: BootstrapPayload | null, i18nData: I18nForRequest | null, opts: BootstrapHtmlOptions): string;
75
152
  declare const flags: {
76
153
  configure(opts: FlagsClientOptions): void;
77
154
  /**
@@ -95,4 +172,4 @@ declare const flags: {
95
172
  evaluate(user: User, rawUrl?: string): BootstrapPayload;
96
173
  };
97
174
 
98
- export { type BootstrapPayload, type ExperimentResult, type FetchLabelsOptions, FlagsClient, type FlagsClientEnv, type FlagsClientOptions, type LabelFile, type User, _resetShipeasyServerForTests, configureShipeasyServer, fetchLabelsForSSR, flags, getShipeasyServerClient, version };
175
+ 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 };
@@ -55,6 +55,32 @@ declare class FlagsClient {
55
55
  */
56
56
  evaluate(user: User, rawUrl?: string): BootstrapPayload;
57
57
  }
58
+ interface I18nForRequest {
59
+ strings: Record<string, string>;
60
+ locale: string;
61
+ }
62
+ declare const i18n: {
63
+ /**
64
+ * Fetch translation labels for the current request and store them in an
65
+ * async-local context so `i18n.t()` / `i18n.tEl()` in SSR'd client
66
+ * components return the real translated strings instead of the key.
67
+ *
68
+ * Call once per request in the root layout (or page). Failure is silent —
69
+ * `i18n.t()` falls back to the hardcoded fallback arg when no labels are
70
+ * loaded.
71
+ *
72
+ * @param key SDK client key (NEXT_PUBLIC_SHIPEASY_CLIENT_KEY)
73
+ * @param profile i18n profile identifier, e.g. "en:prod"
74
+ * @param cdnBaseUrl Optional override for the i18n CDN (default: cdn.i18n.shipeasy.ai)
75
+ */
76
+ init(key: string, profile: string, cdnBaseUrl?: string): Promise<void>;
77
+ /**
78
+ * Return the translation strings loaded for the current request.
79
+ * Use this to include i18n data in the SSR bootstrap payload so the
80
+ * client doesn't need an extra network round-trip.
81
+ */
82
+ getForRequest(): I18nForRequest;
83
+ };
58
84
  interface LabelFile {
59
85
  v: number;
60
86
  profile: string;
@@ -72,6 +98,57 @@ declare function fetchLabelsForSSR(opts: FetchLabelsOptions): Promise<LabelFile
72
98
  declare function configureShipeasyServer(opts: FlagsClientOptions): FlagsClient;
73
99
  declare function getShipeasyServerClient(): FlagsClient | null;
74
100
  declare function _resetShipeasyServerForTests(): void;
101
+ interface ShipeasyServerConfig {
102
+ /**
103
+ * Server-side API key — authenticates flag/experiment fetches from the edge.
104
+ * Never embedded in browser output. A warning is logged if omitted.
105
+ */
106
+ apiKey?: string;
107
+ /**
108
+ * Public client key — embedded in window.__SE_BOOTSTRAP and used by the
109
+ * browser SDK. Safe to expose (e.g. NEXT_PUBLIC_ env vars).
110
+ * Defaults to apiKey for single-key setups.
111
+ */
112
+ clientKey?: string;
113
+ /** Raw URL or query string for applying ?se_ks_* / ?se_cf_* / ?se_exp_* overrides. */
114
+ urlOverrides?: string;
115
+ /** User attributes for flag and experiment evaluation. */
116
+ user?: User;
117
+ /** i18n profile to load for SSR translations, e.g. "en:prod". Defaults to "en:prod". */
118
+ i18nDefaultProfile?: string;
119
+ }
120
+ interface ShipeasyServerHandle {
121
+ flags: Record<string, boolean>;
122
+ configs: Record<string, unknown>;
123
+ experiments: Record<string, ExperimentResult<Record<string, unknown>>>;
124
+ /** Returns a vanilla-JS string for a single inline <script> tag. */
125
+ getBootstrapHtml(): string;
126
+ }
127
+ /**
128
+ * Initialise the ShipEasy server SDK, evaluate flags for this request, and
129
+ * return a handle. Call once per request in your root layout (or page for
130
+ * URL-override support). Failure is non-fatal — evaluation returns empty
131
+ * payloads and i18n falls back to hardcoded strings.
132
+ */
133
+ declare function shipeasy(opts: ShipeasyServerConfig): Promise<ShipeasyServerHandle>;
134
+ interface BootstrapHtmlOptions {
135
+ /** SDK client key */
136
+ apiKey: string;
137
+ /** i18n profile fed to the loader script. Defaults to "en:prod". */
138
+ i18nProfile?: string;
139
+ }
140
+ /**
141
+ * Returns a vanilla-JS script string for a single <script> tag.
142
+ * Handles everything the client needs at startup:
143
+ * - window.__se_devtools_config (when devtoolsAdminUrl is set)
144
+ * - window.__SE_BOOTSTRAP (flags + configs + experiments + i18n + apiKey for auto-init)
145
+ * - window.i18n shim from SSR strings (prevents hydration mismatches)
146
+ * - dynamic <script> injection for the i18n loader
147
+ *
148
+ * Framework-agnostic: set innerHTML on a <script> element, nothing else required.
149
+ * Pass null for bootstrap on pages without flag evaluation — client still auto-inits.
150
+ */
151
+ declare function getBootstrapHtml(bootstrap: BootstrapPayload | null, i18nData: I18nForRequest | null, opts: BootstrapHtmlOptions): string;
75
152
  declare const flags: {
76
153
  configure(opts: FlagsClientOptions): void;
77
154
  /**
@@ -95,4 +172,4 @@ declare const flags: {
95
172
  evaluate(user: User, rawUrl?: string): BootstrapPayload;
96
173
  };
97
174
 
98
- export { type BootstrapPayload, type ExperimentResult, type FetchLabelsOptions, FlagsClient, type FlagsClientEnv, type FlagsClientOptions, type LabelFile, type User, _resetShipeasyServerForTests, configureShipeasyServer, fetchLabelsForSSR, flags, getShipeasyServerClient, version };
175
+ 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 };
@@ -25,10 +25,14 @@ __export(server_exports, {
25
25
  configureShipeasyServer: () => configureShipeasyServer,
26
26
  fetchLabelsForSSR: () => fetchLabelsForSSR,
27
27
  flags: () => flags,
28
+ getBootstrapHtml: () => getBootstrapHtml,
28
29
  getShipeasyServerClient: () => getShipeasyServerClient,
30
+ i18n: () => i18n,
31
+ shipeasy: () => shipeasy,
29
32
  version: () => version
30
33
  });
31
34
  module.exports = __toCommonJS(server_exports);
35
+ var import_node_async_hooks = require("async_hooks");
32
36
  var version = "1.0.0";
33
37
  var C1 = 3432918353;
34
38
  var C2 = 461845907;
@@ -349,6 +353,38 @@ var FlagsClient = class {
349
353
  return { flags: flags2, configs, experiments };
350
354
  }
351
355
  };
356
+ var _I18N_SSR_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:ssr-i18n");
357
+ var _i18nALS = new import_node_async_hooks.AsyncLocalStorage();
358
+ globalThis[_I18N_SSR_SYM] = () => _i18nALS.getStore() ?? null;
359
+ var i18n = {
360
+ /**
361
+ * Fetch translation labels for the current request and store them in an
362
+ * async-local context so `i18n.t()` / `i18n.tEl()` in SSR'd client
363
+ * components return the real translated strings instead of the key.
364
+ *
365
+ * Call once per request in the root layout (or page). Failure is silent —
366
+ * `i18n.t()` falls back to the hardcoded fallback arg when no labels are
367
+ * loaded.
368
+ *
369
+ * @param key SDK client key (NEXT_PUBLIC_SHIPEASY_CLIENT_KEY)
370
+ * @param profile i18n profile identifier, e.g. "en:prod"
371
+ * @param cdnBaseUrl Optional override for the i18n CDN (default: cdn.i18n.shipeasy.ai)
372
+ */
373
+ async init(key, profile, cdnBaseUrl) {
374
+ if (_i18nALS.getStore() !== void 0) return;
375
+ const labels = await fetchLabelsForSSR({ key, profile, cdnBaseUrl }).catch(() => null);
376
+ const locale = profile.split(":")[0] || "en";
377
+ _i18nALS.enterWith({ strings: labels?.strings ?? {}, locale });
378
+ },
379
+ /**
380
+ * Return the translation strings loaded for the current request.
381
+ * Use this to include i18n data in the SSR bootstrap payload so the
382
+ * client doesn't need an extra network round-trip.
383
+ */
384
+ getForRequest() {
385
+ return _i18nALS.getStore() ?? { strings: {}, locale: "en" };
386
+ }
387
+ };
352
388
  var DEFAULT_I18N_CDN = "https://cdn.i18n.shipeasy.ai";
353
389
  async function fetchJson(url, timeoutMs = 2e3) {
354
390
  const controller = new AbortController();
@@ -392,6 +428,51 @@ function _resetShipeasyServerForTests() {
392
428
  _server?.destroy();
393
429
  _server = null;
394
430
  }
431
+ async function shipeasy(opts) {
432
+ if (!opts.apiKey && !opts.clientKey) {
433
+ console.warn("[shipeasy] apiKey is required \u2014 flag evaluation and i18n will not load.");
434
+ } else if (!opts.apiKey) {
435
+ console.warn("[shipeasy] apiKey not set \u2014 falling back to clientKey for server requests.");
436
+ }
437
+ const apiKey = opts.apiKey ?? opts.clientKey ?? "";
438
+ const clientKey = opts.clientKey ?? opts.apiKey ?? "";
439
+ const profile = opts.i18nDefaultProfile ?? "en:prod";
440
+ flags.configure({ apiKey });
441
+ await Promise.allSettled([flags.initOnce(), i18n.init(apiKey, profile)]);
442
+ const bootstrap = flags.evaluate(opts.user ?? {}, opts.urlOverrides);
443
+ const i18nData = i18n.getForRequest();
444
+ return {
445
+ flags: bootstrap.flags,
446
+ configs: bootstrap.configs,
447
+ experiments: bootstrap.experiments,
448
+ getBootstrapHtml() {
449
+ return getBootstrapHtml(bootstrap, i18nData, { apiKey: clientKey });
450
+ }
451
+ };
452
+ }
453
+ function getBootstrapHtml(bootstrap, i18nData, opts) {
454
+ const parts = [];
455
+ const apiUrl = "https://cdn.shipeasy.ai";
456
+ const profile = opts.i18nProfile ?? "en:prod";
457
+ const payload = {
458
+ flags: bootstrap?.flags ?? {},
459
+ configs: bootstrap?.configs ?? {},
460
+ experiments: bootstrap?.experiments ?? {},
461
+ apiKey: opts.apiKey,
462
+ apiUrl
463
+ };
464
+ if (i18nData) payload.i18n = i18nData;
465
+ parts.push(`window.__SE_BOOTSTRAP=${JSON.stringify(payload)};`);
466
+ if (i18nData?.strings && Object.keys(i18nData.strings).length > 0) {
467
+ parts.push(
468
+ `(function(){var d=window.__SE_BOOTSTRAP.i18n;if(!d)return;window.i18n={locale:d.locale,t:function(k,v){var r=d.strings[k];if(!r)return k;return v?r.replace(/\\{\\{(\\w+)\\}\\}/g,function(_,p){return v[p]!==undefined?String(v[p]):'{{'+p+'}}'}):r;},on:function(){return function(){};}};})();`
469
+ );
470
+ }
471
+ parts.push(
472
+ `(function(){var s=document.createElement('script');s.src=${JSON.stringify(`${apiUrl}/sdk/i18n/loader.js`)};s.setAttribute('data-key',${JSON.stringify(opts.apiKey)});s.setAttribute('data-profile',${JSON.stringify(profile)});document.head.appendChild(s);})();`
473
+ );
474
+ return parts.join("");
475
+ }
395
476
  var flags = {
396
477
  configure(opts) {
397
478
  configureShipeasyServer(opts);
@@ -445,6 +526,9 @@ var flags = {
445
526
  configureShipeasyServer,
446
527
  fetchLabelsForSSR,
447
528
  flags,
529
+ getBootstrapHtml,
448
530
  getShipeasyServerClient,
531
+ i18n,
532
+ shipeasy,
449
533
  version
450
534
  });
@@ -1,4 +1,5 @@
1
1
  // src/server/index.ts
2
+ import { AsyncLocalStorage } from "async_hooks";
2
3
  var version = "1.0.0";
3
4
  var C1 = 3432918353;
4
5
  var C2 = 461845907;
@@ -319,6 +320,38 @@ var FlagsClient = class {
319
320
  return { flags: flags2, configs, experiments };
320
321
  }
321
322
  };
323
+ var _I18N_SSR_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:ssr-i18n");
324
+ var _i18nALS = new AsyncLocalStorage();
325
+ globalThis[_I18N_SSR_SYM] = () => _i18nALS.getStore() ?? null;
326
+ var i18n = {
327
+ /**
328
+ * Fetch translation labels for the current request and store them in an
329
+ * async-local context so `i18n.t()` / `i18n.tEl()` in SSR'd client
330
+ * components return the real translated strings instead of the key.
331
+ *
332
+ * Call once per request in the root layout (or page). Failure is silent —
333
+ * `i18n.t()` falls back to the hardcoded fallback arg when no labels are
334
+ * loaded.
335
+ *
336
+ * @param key SDK client key (NEXT_PUBLIC_SHIPEASY_CLIENT_KEY)
337
+ * @param profile i18n profile identifier, e.g. "en:prod"
338
+ * @param cdnBaseUrl Optional override for the i18n CDN (default: cdn.i18n.shipeasy.ai)
339
+ */
340
+ async init(key, profile, cdnBaseUrl) {
341
+ if (_i18nALS.getStore() !== void 0) return;
342
+ const labels = await fetchLabelsForSSR({ key, profile, cdnBaseUrl }).catch(() => null);
343
+ const locale = profile.split(":")[0] || "en";
344
+ _i18nALS.enterWith({ strings: labels?.strings ?? {}, locale });
345
+ },
346
+ /**
347
+ * Return the translation strings loaded for the current request.
348
+ * Use this to include i18n data in the SSR bootstrap payload so the
349
+ * client doesn't need an extra network round-trip.
350
+ */
351
+ getForRequest() {
352
+ return _i18nALS.getStore() ?? { strings: {}, locale: "en" };
353
+ }
354
+ };
322
355
  var DEFAULT_I18N_CDN = "https://cdn.i18n.shipeasy.ai";
323
356
  async function fetchJson(url, timeoutMs = 2e3) {
324
357
  const controller = new AbortController();
@@ -362,6 +395,51 @@ function _resetShipeasyServerForTests() {
362
395
  _server?.destroy();
363
396
  _server = null;
364
397
  }
398
+ async function shipeasy(opts) {
399
+ if (!opts.apiKey && !opts.clientKey) {
400
+ console.warn("[shipeasy] apiKey is required \u2014 flag evaluation and i18n will not load.");
401
+ } else if (!opts.apiKey) {
402
+ console.warn("[shipeasy] apiKey not set \u2014 falling back to clientKey for server requests.");
403
+ }
404
+ const apiKey = opts.apiKey ?? opts.clientKey ?? "";
405
+ const clientKey = opts.clientKey ?? opts.apiKey ?? "";
406
+ const profile = opts.i18nDefaultProfile ?? "en:prod";
407
+ flags.configure({ apiKey });
408
+ await Promise.allSettled([flags.initOnce(), i18n.init(apiKey, profile)]);
409
+ const bootstrap = flags.evaluate(opts.user ?? {}, opts.urlOverrides);
410
+ const i18nData = i18n.getForRequest();
411
+ return {
412
+ flags: bootstrap.flags,
413
+ configs: bootstrap.configs,
414
+ experiments: bootstrap.experiments,
415
+ getBootstrapHtml() {
416
+ return getBootstrapHtml(bootstrap, i18nData, { apiKey: clientKey });
417
+ }
418
+ };
419
+ }
420
+ function getBootstrapHtml(bootstrap, i18nData, opts) {
421
+ const parts = [];
422
+ const apiUrl = "https://cdn.shipeasy.ai";
423
+ const profile = opts.i18nProfile ?? "en:prod";
424
+ const payload = {
425
+ flags: bootstrap?.flags ?? {},
426
+ configs: bootstrap?.configs ?? {},
427
+ experiments: bootstrap?.experiments ?? {},
428
+ apiKey: opts.apiKey,
429
+ apiUrl
430
+ };
431
+ if (i18nData) payload.i18n = i18nData;
432
+ parts.push(`window.__SE_BOOTSTRAP=${JSON.stringify(payload)};`);
433
+ if (i18nData?.strings && Object.keys(i18nData.strings).length > 0) {
434
+ parts.push(
435
+ `(function(){var d=window.__SE_BOOTSTRAP.i18n;if(!d)return;window.i18n={locale:d.locale,t:function(k,v){var r=d.strings[k];if(!r)return k;return v?r.replace(/\\{\\{(\\w+)\\}\\}/g,function(_,p){return v[p]!==undefined?String(v[p]):'{{'+p+'}}'}):r;},on:function(){return function(){};}};})();`
436
+ );
437
+ }
438
+ parts.push(
439
+ `(function(){var s=document.createElement('script');s.src=${JSON.stringify(`${apiUrl}/sdk/i18n/loader.js`)};s.setAttribute('data-key',${JSON.stringify(opts.apiKey)});s.setAttribute('data-profile',${JSON.stringify(profile)});document.head.appendChild(s);})();`
440
+ );
441
+ return parts.join("");
442
+ }
365
443
  var flags = {
366
444
  configure(opts) {
367
445
  configureShipeasyServer(opts);
@@ -414,6 +492,9 @@ export {
414
492
  configureShipeasyServer,
415
493
  fetchLabelsForSSR,
416
494
  flags,
495
+ getBootstrapHtml,
417
496
  getShipeasyServerClient,
497
+ i18n,
498
+ shipeasy,
418
499
  version
419
500
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipeasy/sdk",
3
- "version": "2.0.3",
3
+ "version": "2.1.1",
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",
@@ -48,13 +48,6 @@
48
48
  "LICENSE",
49
49
  "README.md"
50
50
  ],
51
- "scripts": {
52
- "build": "tsup",
53
- "type-check": "tsc --noEmit",
54
- "test": "vitest run",
55
- "test:watch": "vitest",
56
- "publish-loader": "wrangler r2 object put shipeasy-sdk/loader-v$npm_package_version.js --file=dist/loader/loader.global.js --content-type 'application/javascript; charset=utf-8' --cache-control 'public, max-age=31536000, immutable' --remote && wrangler r2 object put shipeasy-sdk/loader.js --file=dist/loader/loader.global.js --content-type 'application/javascript; charset=utf-8' --cache-control 'public, max-age=300' --remote"
57
- },
58
51
  "dependencies": {
59
52
  "murmurhash-js": "^1.0.0"
60
53
  },
@@ -78,5 +71,12 @@
78
71
  },
79
72
  "engines": {
80
73
  "node": ">=20"
74
+ },
75
+ "scripts": {
76
+ "build": "tsup",
77
+ "type-check": "tsc --noEmit",
78
+ "test": "vitest run",
79
+ "test:watch": "vitest",
80
+ "publish-loader": "wrangler r2 object put shipeasy-sdk/loader-v$npm_package_version.js --file=dist/loader/loader.global.js --content-type 'application/javascript; charset=utf-8' --cache-control 'public, max-age=31536000, immutable' --remote && wrangler r2 object put shipeasy-sdk/loader.js --file=dist/loader/loader.global.js --content-type 'application/javascript; charset=utf-8' --cache-control 'public, max-age=300' --remote"
81
81
  }
82
- }
82
+ }